You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

images.c 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. /*-
  2. * Copyright 2016 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "config.h"
  17. #include "images.h"
  18. #include "task.h"
  19. #include "message.h"
  20. #include "libserver/html/html.h"
  21. #define msg_debug_images(...) rspamd_conditional_debug_fast (NULL, NULL, \
  22. rspamd_images_log_id, "images", task->task_pool->tag.uid, \
  23. G_STRFUNC, \
  24. __VA_ARGS__)
  25. INIT_LOG_MODULE(images)
  26. #ifdef USABLE_GD
  27. #include "gd.h"
  28. #include "hash.h"
  29. #include <math.h>
  30. #define RSPAMD_NORMALIZED_DIM 64
  31. static rspamd_lru_hash_t *images_hash = NULL;
  32. #endif
  33. static const guint8 png_signature[] = {137, 80, 78, 71, 13, 10, 26, 10};
  34. static const guint8 jpg_sig1[] = {0xff, 0xd8};
  35. static const guint8 jpg_sig_jfif[] = {0xff, 0xe0};
  36. static const guint8 jpg_sig_exif[] = {0xff, 0xe1};
  37. static const guint8 gif_signature[] = {'G', 'I', 'F', '8'};
  38. static const guint8 bmp_signature[] = {'B', 'M'};
  39. static bool process_image (struct rspamd_task *task, struct rspamd_mime_part *part);
  40. bool
  41. rspamd_images_process_mime_part_maybe (struct rspamd_task *task,
  42. struct rspamd_mime_part *part)
  43. {
  44. if (part->part_type == RSPAMD_MIME_PART_UNDEFINED) {
  45. if (part->detected_type &&
  46. strcmp (part->detected_type, "image") == 0 &&
  47. part->parsed_data.len > 0) {
  48. return process_image (task, part);
  49. }
  50. }
  51. return false;
  52. }
  53. void
  54. rspamd_images_process (struct rspamd_task *task)
  55. {
  56. guint i;
  57. struct rspamd_mime_part *part;
  58. PTR_ARRAY_FOREACH (MESSAGE_FIELD (task, parts), i, part) {
  59. rspamd_images_process_mime_part_maybe (task, part);
  60. }
  61. }
  62. static enum rspamd_image_type
  63. detect_image_type (rspamd_ftok_t *data)
  64. {
  65. if (data->len > sizeof (png_signature) / sizeof (png_signature[0])) {
  66. if (memcmp (data->begin, png_signature, sizeof (png_signature)) == 0) {
  67. return IMAGE_TYPE_PNG;
  68. }
  69. }
  70. if (data->len > 10) {
  71. if (memcmp (data->begin, jpg_sig1, sizeof (jpg_sig1)) == 0) {
  72. if (memcmp (data->begin + 2, jpg_sig_jfif, sizeof (jpg_sig_jfif)) == 0 ||
  73. memcmp (data->begin + 2, jpg_sig_exif, sizeof (jpg_sig_exif)) == 0) {
  74. return IMAGE_TYPE_JPG;
  75. }
  76. }
  77. }
  78. if (data->len > sizeof (gif_signature) / sizeof (gif_signature[0])) {
  79. if (memcmp (data->begin, gif_signature, sizeof (gif_signature)) == 0) {
  80. return IMAGE_TYPE_GIF;
  81. }
  82. }
  83. if (data->len > sizeof (bmp_signature) / sizeof (bmp_signature[0])) {
  84. if (memcmp (data->begin, bmp_signature, sizeof (bmp_signature)) == 0) {
  85. return IMAGE_TYPE_BMP;
  86. }
  87. }
  88. return IMAGE_TYPE_UNKNOWN;
  89. }
  90. static struct rspamd_image *
  91. process_png_image (rspamd_mempool_t *pool, rspamd_ftok_t *data)
  92. {
  93. struct rspamd_image *img;
  94. guint32 t;
  95. const guint8 *p;
  96. if (data->len < 24) {
  97. msg_info_pool ("bad png detected (maybe striped)");
  98. return NULL;
  99. }
  100. /* In png we should find iHDR section and get data from it */
  101. /* Skip signature and read header section */
  102. p = data->begin + 12;
  103. if (memcmp (p, "IHDR", 4) != 0) {
  104. msg_info_pool ("png doesn't begins with IHDR section");
  105. return NULL;
  106. }
  107. img = rspamd_mempool_alloc0 (pool, sizeof (struct rspamd_image));
  108. img->type = IMAGE_TYPE_PNG;
  109. img->data = data;
  110. p += 4;
  111. memcpy (&t, p, sizeof (guint32));
  112. img->width = ntohl (t);
  113. p += 4;
  114. memcpy (&t, p, sizeof (guint32));
  115. img->height = ntohl (t);
  116. return img;
  117. }
  118. static struct rspamd_image *
  119. process_jpg_image (rspamd_mempool_t *pool, rspamd_ftok_t *data)
  120. {
  121. const guint8 *p, *end;
  122. guint16 h, w;
  123. struct rspamd_image *img;
  124. img = rspamd_mempool_alloc0 (pool, sizeof (struct rspamd_image));
  125. img->type = IMAGE_TYPE_JPG;
  126. img->data = data;
  127. p = data->begin;
  128. end = p + data->len - 8;
  129. p += 2;
  130. while (p < end) {
  131. if (p[0] == 0xFF && p[1] != 0xFF) {
  132. guint len = p[2] * 256 + p[3];
  133. p ++;
  134. if (*p == 0xc0 || *p == 0xc1 || *p == 0xc2 || *p == 0xc3 ||
  135. *p == 0xc9 || *p == 0xca || *p == 0xcb) {
  136. memcpy (&h, p + 4, sizeof (guint16));
  137. h = p[4] * 0xff + p[5];
  138. img->height = h;
  139. w = p[6] * 0xff + p[7];
  140. img->width = w;
  141. return img;
  142. }
  143. p += len;
  144. }
  145. else {
  146. p++;
  147. }
  148. }
  149. return NULL;
  150. }
  151. static struct rspamd_image *
  152. process_gif_image (rspamd_mempool_t *pool, rspamd_ftok_t *data)
  153. {
  154. struct rspamd_image *img;
  155. const guint8 *p;
  156. guint16 t;
  157. if (data->len < 10) {
  158. msg_info_pool ("bad gif detected (maybe striped)");
  159. return NULL;
  160. }
  161. img = rspamd_mempool_alloc0 (pool, sizeof (struct rspamd_image));
  162. img->type = IMAGE_TYPE_GIF;
  163. img->data = data;
  164. p = data->begin + 6;
  165. memcpy (&t, p, sizeof (guint16));
  166. img->width = GUINT16_FROM_LE (t);
  167. memcpy (&t, p + 2, sizeof (guint16));
  168. img->height = GUINT16_FROM_LE (t);
  169. return img;
  170. }
  171. static struct rspamd_image *
  172. process_bmp_image (rspamd_mempool_t *pool, rspamd_ftok_t *data)
  173. {
  174. struct rspamd_image *img;
  175. gint32 t;
  176. const guint8 *p;
  177. if (data->len < 28) {
  178. msg_info_pool ("bad bmp detected (maybe striped)");
  179. return NULL;
  180. }
  181. img = rspamd_mempool_alloc0 (pool, sizeof (struct rspamd_image));
  182. img->type = IMAGE_TYPE_BMP;
  183. img->data = data;
  184. p = data->begin + 18;
  185. memcpy (&t, p, sizeof (guint32));
  186. img->width = GUINT32_FROM_LE (t);
  187. memcpy (&t, p + 4, sizeof (gint32));
  188. img->height = GUINT32_FROM_LE (t);
  189. return img;
  190. }
  191. #ifdef USABLE_GD
  192. /*
  193. * DCT from Emil Mikulic.
  194. * http://unix4lyfe.org/dct/
  195. */
  196. static void
  197. rspamd_image_dct_block (gint pixels[8][8], gdouble *out)
  198. {
  199. gint i;
  200. gint rows[8][8];
  201. static const gint c1 = 1004 /* cos(pi/16) << 10 */,
  202. s1 = 200 /* sin(pi/16) */,
  203. c3 = 851 /* cos(3pi/16) << 10 */,
  204. s3 = 569 /* sin(3pi/16) << 10 */,
  205. r2c6 = 554 /* sqrt(2)*cos(6pi/16) << 10 */,
  206. r2s6 = 1337 /* sqrt(2)*sin(6pi/16) << 10 */,
  207. r2 = 181; /* sqrt(2) << 7*/
  208. gint x0, x1, x2, x3, x4, x5, x6, x7, x8;
  209. /* transform rows */
  210. for (i = 0; i < 8; i++) {
  211. x0 = pixels[0][i];
  212. x1 = pixels[1][i];
  213. x2 = pixels[2][i];
  214. x3 = pixels[3][i];
  215. x4 = pixels[4][i];
  216. x5 = pixels[5][i];
  217. x6 = pixels[6][i];
  218. x7 = pixels[7][i];
  219. /* Stage 1 */
  220. x8 = x7 + x0;
  221. x0 -= x7;
  222. x7 = x1 + x6;
  223. x1 -= x6;
  224. x6 = x2 + x5;
  225. x2 -= x5;
  226. x5 = x3 + x4;
  227. x3 -= x4;
  228. /* Stage 2 */
  229. x4 = x8 + x5;
  230. x8 -= x5;
  231. x5 = x7 + x6;
  232. x7 -= x6;
  233. x6 = c1 * (x1 + x2);
  234. x2 = (-s1 - c1) * x2 + x6;
  235. x1 = (s1 - c1) * x1 + x6;
  236. x6 = c3 * (x0 + x3);
  237. x3 = (-s3 - c3) * x3 + x6;
  238. x0 = (s3 - c3) * x0 + x6;
  239. /* Stage 3 */
  240. x6 = x4 + x5;
  241. x4 -= x5;
  242. x5 = r2c6 * (x7 + x8);
  243. x7 = (-r2s6 - r2c6) * x7 + x5;
  244. x8 = (r2s6 - r2c6) * x8 + x5;
  245. x5 = x0 + x2;
  246. x0 -= x2;
  247. x2 = x3 + x1;
  248. x3 -= x1;
  249. /* Stage 4 and output */
  250. rows[i][0] = x6;
  251. rows[i][4] = x4;
  252. rows[i][2] = x8 >> 10;
  253. rows[i][6] = x7 >> 10;
  254. rows[i][7] = (x2 - x5) >> 10;
  255. rows[i][1] = (x2 + x5) >> 10;
  256. rows[i][3] = (x3 * r2) >> 17;
  257. rows[i][5] = (x0 * r2) >> 17;
  258. }
  259. /* transform columns */
  260. for (i = 0; i < 8; i++) {
  261. x0 = rows[0][i];
  262. x1 = rows[1][i];
  263. x2 = rows[2][i];
  264. x3 = rows[3][i];
  265. x4 = rows[4][i];
  266. x5 = rows[5][i];
  267. x6 = rows[6][i];
  268. x7 = rows[7][i];
  269. /* Stage 1 */
  270. x8 = x7 + x0;
  271. x0 -= x7;
  272. x7 = x1 + x6;
  273. x1 -= x6;
  274. x6 = x2 + x5;
  275. x2 -= x5;
  276. x5 = x3 + x4;
  277. x3 -= x4;
  278. /* Stage 2 */
  279. x4 = x8 + x5;
  280. x8 -= x5;
  281. x5 = x7 + x6;
  282. x7 -= x6;
  283. x6 = c1 * (x1 + x2);
  284. x2 = (-s1 - c1) * x2 + x6;
  285. x1 = (s1 - c1) * x1 + x6;
  286. x6 = c3 * (x0 + x3);
  287. x3 = (-s3 - c3) * x3 + x6;
  288. x0 = (s3 - c3) * x0 + x6;
  289. /* Stage 3 */
  290. x6 = x4 + x5;
  291. x4 -= x5;
  292. x5 = r2c6 * (x7 + x8);
  293. x7 = (-r2s6 - r2c6) * x7 + x5;
  294. x8 = (r2s6 - r2c6) * x8 + x5;
  295. x5 = x0 + x2;
  296. x0 -= x2;
  297. x2 = x3 + x1;
  298. x3 -= x1;
  299. /* Stage 4 and output */
  300. out[i * 8] = (double) ((x6 + 16) >> 3);
  301. out[i * 8 + 1] = (double) ((x4 + 16) >> 3);
  302. out[i * 8 + 2] = (double) ((x8 + 16384) >> 13);
  303. out[i * 8 + 3] = (double) ((x7 + 16384) >> 13);
  304. out[i * 8 + 4] = (double) ((x2 - x5 + 16384) >> 13);
  305. out[i * 8 + 5] = (double) ((x2 + x5 + 16384) >> 13);
  306. out[i * 8 + 6] = (double) (((x3 >> 8) * r2 + 8192) >> 12);
  307. out[i * 8 + 7] = (double) (((x0 >> 8) * r2 + 8192) >> 12);
  308. }
  309. }
  310. struct rspamd_image_cache_entry {
  311. guchar digest[64];
  312. guchar dct[RSPAMD_DCT_LEN / NBBY];
  313. };
  314. static void
  315. rspamd_image_cache_entry_dtor (gpointer p)
  316. {
  317. struct rspamd_image_cache_entry *entry = p;
  318. g_free (entry);
  319. }
  320. static guint32
  321. rspamd_image_dct_hash (gconstpointer p)
  322. {
  323. return rspamd_cryptobox_fast_hash (p, rspamd_cryptobox_HASHBYTES,
  324. rspamd_hash_seed ());
  325. }
  326. static gboolean
  327. rspamd_image_dct_equal (gconstpointer a, gconstpointer b)
  328. {
  329. return memcmp (a, b, rspamd_cryptobox_HASHBYTES) == 0;
  330. }
  331. static void
  332. rspamd_image_create_cache (struct rspamd_config *cfg)
  333. {
  334. images_hash = rspamd_lru_hash_new_full (cfg->images_cache_size, NULL,
  335. rspamd_image_cache_entry_dtor,
  336. rspamd_image_dct_hash, rspamd_image_dct_equal);
  337. }
  338. static gboolean
  339. rspamd_image_check_hash (struct rspamd_task *task, struct rspamd_image *img)
  340. {
  341. struct rspamd_image_cache_entry *found;
  342. if (images_hash == NULL) {
  343. rspamd_image_create_cache (task->cfg);
  344. }
  345. found = rspamd_lru_hash_lookup (images_hash, img->parent->digest,
  346. task->tv.tv_sec);
  347. if (found) {
  348. /* We need to decompress */
  349. img->dct = g_malloc (RSPAMD_DCT_LEN / NBBY);
  350. rspamd_mempool_add_destructor (task->task_pool, g_free,
  351. img->dct);
  352. /* Copy as found could be destroyed by LRU */
  353. memcpy (img->dct, found->dct, RSPAMD_DCT_LEN / NBBY);
  354. img->is_normalized = TRUE;
  355. return TRUE;
  356. }
  357. return FALSE;
  358. }
  359. static void
  360. rspamd_image_save_hash (struct rspamd_task *task, struct rspamd_image *img)
  361. {
  362. struct rspamd_image_cache_entry *found;
  363. if (img->is_normalized) {
  364. found = rspamd_lru_hash_lookup (images_hash, img->parent->digest,
  365. task->tv.tv_sec);
  366. if (!found) {
  367. found = g_malloc0 (sizeof (*found));
  368. memcpy (found->dct, img->dct, RSPAMD_DCT_LEN / NBBY);
  369. memcpy (found->digest, img->parent->digest, sizeof (found->digest));
  370. rspamd_lru_hash_insert (images_hash, found->digest, found,
  371. task->tv.tv_sec, 0);
  372. }
  373. }
  374. }
  375. #endif
  376. void
  377. rspamd_image_normalize (struct rspamd_task *task, struct rspamd_image *img)
  378. {
  379. #ifdef USABLE_GD
  380. gdImagePtr src = NULL, dst = NULL;
  381. guint i, j, k, l;
  382. gdouble *dct;
  383. if (img->data->len == 0 || img->data->len > G_MAXINT32) {
  384. return;
  385. }
  386. if (img->height <= RSPAMD_NORMALIZED_DIM ||
  387. img->width <= RSPAMD_NORMALIZED_DIM) {
  388. return;
  389. }
  390. if (img->data->len > task->cfg->max_pic_size) {
  391. return;
  392. }
  393. if (rspamd_image_check_hash (task, img)) {
  394. return;
  395. }
  396. switch (img->type) {
  397. case IMAGE_TYPE_JPG:
  398. src = gdImageCreateFromJpegPtr (img->data->len, (void *)img->data->begin);
  399. break;
  400. case IMAGE_TYPE_PNG:
  401. src = gdImageCreateFromPngPtr (img->data->len, (void *)img->data->begin);
  402. break;
  403. case IMAGE_TYPE_GIF:
  404. src = gdImageCreateFromGifPtr (img->data->len, (void *)img->data->begin);
  405. break;
  406. case IMAGE_TYPE_BMP:
  407. src = gdImageCreateFromBmpPtr (img->data->len, (void *)img->data->begin);
  408. break;
  409. default:
  410. return;
  411. }
  412. if (src == NULL) {
  413. msg_info_task ("cannot load image of type %s from %T",
  414. rspamd_image_type_str (img->type), img->filename);
  415. }
  416. else {
  417. gdImageSetInterpolationMethod (src, GD_BILINEAR_FIXED);
  418. dst = gdImageScale (src, RSPAMD_NORMALIZED_DIM, RSPAMD_NORMALIZED_DIM);
  419. gdImageGrayScale (dst);
  420. gdImageDestroy (src);
  421. img->is_normalized = TRUE;
  422. dct = g_malloc0 (sizeof (gdouble) * RSPAMD_DCT_LEN);
  423. img->dct = g_malloc0 (RSPAMD_DCT_LEN / NBBY);
  424. rspamd_mempool_add_destructor (task->task_pool, g_free,
  425. img->dct);
  426. /*
  427. * Split message into blocks:
  428. *
  429. * ****
  430. * ****
  431. *
  432. * Get sum of saturation values, and set bit if sum is > avg
  433. * Then go further
  434. *
  435. * ****
  436. * ****
  437. *
  438. * and repeat this algorithm.
  439. *
  440. * So on each iteration we move by 16 pixels and calculate 2 elements of
  441. * signature
  442. */
  443. for (i = 0; i < RSPAMD_NORMALIZED_DIM; i += 8) {
  444. for (j = 0; j < RSPAMD_NORMALIZED_DIM; j += 8) {
  445. gint p[8][8];
  446. for (k = 0; k < 8; k ++) {
  447. p[k][0] = gdImageGetPixel (dst, i + k, j);
  448. p[k][1] = gdImageGetPixel (dst, i + k, j + 1);
  449. p[k][2] = gdImageGetPixel (dst, i + k, j + 2);
  450. p[k][3] = gdImageGetPixel (dst, i + k, j + 3);
  451. p[k][4] = gdImageGetPixel (dst, i + k, j + 4);
  452. p[k][5] = gdImageGetPixel (dst, i + k, j + 5);
  453. p[k][6] = gdImageGetPixel (dst, i + k, j + 6);
  454. p[k][7] = gdImageGetPixel (dst, i + k, j + 7);
  455. }
  456. rspamd_image_dct_block (p,
  457. dct + i * RSPAMD_NORMALIZED_DIM + j);
  458. gdouble avg = 0.0;
  459. for (k = 0; k < 8; k ++) {
  460. for (l = 0; l < 8; l ++) {
  461. gdouble x = *(dct +
  462. i * RSPAMD_NORMALIZED_DIM + j + k * 8 + l);
  463. avg += (x - avg) / (gdouble)(k * 8 + l + 1);
  464. }
  465. }
  466. for (k = 0; k < 8; k ++) {
  467. for (l = 0; l < 8; l ++) {
  468. guint idx = i * RSPAMD_NORMALIZED_DIM + j + k * 8 + l;
  469. if (dct[idx] >= avg) {
  470. setbit (img->dct, idx);
  471. }
  472. }
  473. }
  474. }
  475. }
  476. gdImageDestroy (dst);
  477. g_free (dct);
  478. rspamd_image_save_hash (task, img);
  479. }
  480. #endif
  481. }
  482. struct rspamd_image*
  483. rspamd_maybe_process_image (rspamd_mempool_t *pool,
  484. rspamd_ftok_t *data)
  485. {
  486. enum rspamd_image_type type;
  487. struct rspamd_image *img = NULL;
  488. if ((type = detect_image_type (data)) != IMAGE_TYPE_UNKNOWN) {
  489. switch (type) {
  490. case IMAGE_TYPE_PNG:
  491. img = process_png_image (pool, data);
  492. break;
  493. case IMAGE_TYPE_JPG:
  494. img = process_jpg_image (pool, data);
  495. break;
  496. case IMAGE_TYPE_GIF:
  497. img = process_gif_image (pool, data);
  498. break;
  499. case IMAGE_TYPE_BMP:
  500. img = process_bmp_image (pool, data);
  501. break;
  502. default:
  503. img = NULL;
  504. break;
  505. }
  506. }
  507. return img;
  508. }
  509. static bool
  510. process_image (struct rspamd_task *task, struct rspamd_mime_part *part)
  511. {
  512. struct rspamd_image *img;
  513. img = rspamd_maybe_process_image (task->task_pool, &part->parsed_data);
  514. if (img != NULL) {
  515. msg_debug_images ("detected %s image of size %ud x %ud",
  516. rspamd_image_type_str (img->type),
  517. img->width, img->height);
  518. if (part->cd) {
  519. img->filename = &part->cd->filename;
  520. }
  521. img->parent = part;
  522. part->part_type = RSPAMD_MIME_PART_IMAGE;
  523. part->specific.img = img;
  524. return true;
  525. }
  526. return false;
  527. }
  528. const gchar *
  529. rspamd_image_type_str (enum rspamd_image_type type)
  530. {
  531. switch (type) {
  532. case IMAGE_TYPE_PNG:
  533. return "PNG";
  534. break;
  535. case IMAGE_TYPE_JPG:
  536. return "JPEG";
  537. break;
  538. case IMAGE_TYPE_GIF:
  539. return "GIF";
  540. break;
  541. case IMAGE_TYPE_BMP:
  542. return "BMP";
  543. break;
  544. default:
  545. break;
  546. }
  547. return "unknown";
  548. }
  549. static void
  550. rspamd_image_process_part (struct rspamd_task *task, struct rspamd_mime_part *part)
  551. {
  552. struct rspamd_mime_header *rh;
  553. struct rspamd_mime_text_part *tp;
  554. struct html_image *himg;
  555. const gchar *cid;
  556. guint cid_len, i;
  557. struct rspamd_image *img;
  558. img = (struct rspamd_image *)part->specific.img;
  559. if (img) {
  560. /* Check Content-Id */
  561. rh = rspamd_message_get_header_from_hash(part->raw_headers,
  562. "Content-Id", FALSE);
  563. if (rh) {
  564. cid = rh->decoded;
  565. if (*cid == '<') {
  566. cid ++;
  567. }
  568. cid_len = strlen (cid);
  569. if (cid_len > 0) {
  570. if (cid[cid_len - 1] == '>') {
  571. cid_len --;
  572. }
  573. PTR_ARRAY_FOREACH (MESSAGE_FIELD (task, text_parts), i, tp) {
  574. if (IS_TEXT_PART_HTML (tp) && tp->html != NULL) {
  575. himg = rspamd_html_find_embedded_image(tp->html, cid, cid_len);
  576. if (himg != NULL) {
  577. img->html_image = himg;
  578. himg->embedded_image = img;
  579. msg_debug_images ("found linked image by cid: <%s>",
  580. cid);
  581. if (himg->height == 0) {
  582. himg->height = img->height;
  583. }
  584. if (himg->width == 0) {
  585. himg->width = img->width;
  586. }
  587. }
  588. }
  589. }
  590. }
  591. }
  592. }
  593. }
  594. void
  595. rspamd_images_link (struct rspamd_task *task)
  596. {
  597. struct rspamd_mime_part *part;
  598. guint i;
  599. PTR_ARRAY_FOREACH (MESSAGE_FIELD (task, parts), i, part) {
  600. if (part->part_type == RSPAMD_MIME_PART_IMAGE) {
  601. rspamd_image_process_part (task, part);
  602. }
  603. }
  604. }