/[pkg-mixmaster]/trunk/Mix/Src/mime.c
ViewVC logotype

Contents of /trunk/Mix/Src/mime.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show annotations) (download)
Wed Oct 31 08:19:51 2001 UTC (11 years, 6 months ago) by rabbi
File MIME type: text/plain
File size: 18621 byte(s)
Initial revision
1 /* Mixmaster version 3 -- (C) 1999 Anonymizer Inc.
2
3 Mixmaster may be redistributed and modified under certain conditions.
4 This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
5 ANY KIND, either express or implied. See the file COPYRIGHT for
6 details.
7
8 MIME functions
9 $Id: mime.c,v 1.1 2001/10/31 08:19:53 rabbi Exp $ */
10
11
12 #include "mix3.h"
13 #include <ctype.h>
14
15 #define hex(i) (isdigit(i) ? (i) - '0' : tolower(i) - 'a' + 10)
16
17 #define hexdigit(i) ((byte)(i < 10 ? i + '0' : i - 10 + 'A'))
18
19 static void encode_word(BUFFER *in)
20 {
21 BUFFER *out;
22 int i;
23
24 out = buf_new();
25 for (i = 0; i < in->length; i++)
26 if (in->data[i] < 32 || in->data[i] >= 127 || in->data[i] == '='
27 || in->data[i] == '?' || in->data[i] == '_') {
28 buf_appendc(out, '=');
29 buf_appendc(out, hexdigit(in->data[i] / 16));
30 buf_appendc(out, hexdigit(in->data[i] % 16));
31 } else if (in->data[i] == ' ')
32 buf_appendc(out, '_');
33 else
34 buf_appendc(out, in->data[i]);
35 buf_move(in, out);
36 buf_free(out);
37 }
38
39 void body_encode(BUFFER *in, int transport, BUFFER *hdr)
40 {
41 BUFFER *out;
42 int c, l=0, encoding = 0;
43 out = buf_new();
44
45 buf_clear(hdr);
46
47 l = in->ptr;
48 while ((c = buf_getc(in)) != -1 && encoding != 2) {
49 if (c >= 160)
50 encoding = 1;
51 else if (c == ' ') {
52 if (buf_getc(in) == '\n')
53 encoding = 1;
54 buf_ungetc(in);
55 } else if ((c < 32 && c != ' ' && c != '\n' && c != '\t') ||
56 (c >= 127 && c < 160)) {
57 encoding = 2;
58 }
59 }
60 in->ptr = l;
61
62 if (encoding == 2) {
63 buf_sets(hdr, "Content-Transfer-Encoding: base64\n");
64 encode(in, 76);
65 } else {
66
67 #if 0
68 if (encoding == 0)
69 buf_sets(hdr, "Content-Transfer-Encoding: 7bit\n");
70 #endif
71 if (encoding != 0 && transport == MIME_8BIT)
72 buf_sets(hdr, "Content-Transfer-Encoding: 8bit\n");
73 if (encoding == 0 || transport == MIME_8BIT) {
74 buf_rest(out, in); /* transparent */
75 buf_move(in, out);
76 } else {
77 buf_sets(hdr, "Content-Transfer-Encoding: quoted-printable\n");
78 l = 0;
79 while ((c = buf_getc(in)) != -1) {
80 if (c == '\n') {
81 buf_nl(out);
82 l = 0;
83 }
84 else if (c < 32 || c >= 127 || c == '=') {
85 if (l > 73) {
86 buf_appends(out, "=\n");
87 l = 0;
88 }
89 buf_appendc(out, '=');
90 buf_appendc(out, hexdigit(c / 16));
91 buf_appendc(out, hexdigit(c % 16));
92 l += 3;
93 } else if (c == ' ') {
94 if (buf_getc(in) == '\n') {
95 buf_appendc(out, '=');
96 buf_appendc(out, hexdigit(c / 16));
97 buf_appendc(out, hexdigit(c % 16));
98 buf_nl(out);
99 l = 0;
100 } else {
101 buf_appendc(out, c);
102 buf_ungetc(in);
103 l++;
104 }
105 } else {
106 buf_appendc(out, c);
107 l++;
108 }
109 }
110 buf_move(in, out);
111 }
112 }
113 buf_free(out);
114 }
115
116 int mail_encode(BUFFER *in, int encoding)
117 {
118 BUFFER *out, *line, *tmp;
119
120 out = buf_new();
121 line = buf_new();
122 tmp = buf_new();
123
124 while (buf_getline(in, line) == 0) {
125 hdr_encode(line, 255);
126 buf_cat(out, line);
127 buf_nl(out);
128 }
129 if (in->ptr < in->length) {
130 /* no newline if only encoding header lines */
131 if (encoding == 0) {
132 buf_nl(out);
133 buf_rest(out, in);
134 }
135 else {
136 body_encode(in, encoding, line);
137 buf_cat(out, line);
138 buf_nl(out);
139 buf_cat(out, in);
140 }
141 }
142 buf_move(in, out);
143 buf_free(line);
144 buf_free(tmp);
145 buf_free(out);
146 return (0);
147 }
148
149 int hdr_encode(BUFFER *in, int n)
150 {
151 int i;
152 int encodeword = 0, encode = 0;
153 BUFFER *out, *word, *space;
154
155 out = buf_new();
156 word = buf_new();
157 space = buf_new();
158 for (i = 0; i <= in->length; i++) {
159 if (isspace(in->data[i]) || in->data[i] == '\0') {
160 if (word->length) {
161 if (encodeword) {
162 if (encode == 0) {
163 buf_cat(out, space);
164 buf_clear(space);
165 buf_appends(out, "=?");
166 buf_appends(out, MIMECHARSET);
167 buf_appends(out, "?Q?");
168 encode = 1;
169 } else {
170 buf_cat(space, word);
171 buf_move(word, space);
172 }
173 encode_word(word);
174 }
175 if (encode && !encodeword) {
176 encode = 0;
177 buf_appends(out, "?=");
178 }
179 buf_cat(out, space);
180 buf_cat(out, word);
181 encodeword = 0;
182 buf_clear(space);
183 buf_clear(word);
184 }
185 buf_appendc(space, in->data[i]);
186 } else {
187 if (in->data[i] < 32 || in->data[i] >= 127)
188 encodeword = 1;
189 buf_appendc(word, in->data[i]);
190 }
191 }
192 if (encode)
193 buf_appends(out, "?=");
194
195 buf_move(in, out);
196 while (n > 0 && in->length - in->ptr > n) {
197 for (i = 1; i < in->length - in->ptr; i++)
198 if (isspace(in->data[in->length - i]))
199 break;
200 buf_get(in, out, in->length - i);
201 buf_appends(out, "\n\t");
202 }
203 buf_rest(out, in);
204 buf_move(in, out);
205 buf_free(out);
206 buf_free(space);
207 buf_free(word);
208 return (0);
209 }
210
211 void addprintable(BUFFER *out, int c)
212 {
213 if (c == '\n')
214 buf_appendc(out, (char) c);
215 else if (c == '\t')
216 buf_appends(out, " ");
217 else if (c == '\014')
218 buf_appends(out, "^L");
219 else if (c == '\r') ;
220 else if (c <= 31 || (c >= 128 && c <= 128 + 31))
221 buf_appendc(out, '?');
222 else
223 buf_appendc(out, (char) c);
224 }
225
226 void addprintablebuf(BUFFER *out, BUFFER *in)
227 {
228 int c;
229
230 while ((c = buf_getc(in)) != -1)
231 addprintable(out, c);
232 }
233
234 int decode_line(BUFFER *line)
235 {
236 BUFFER *out;
237 unsigned int i;
238 int c, softbreak = 0;
239
240 out = buf_new();
241 for (i = 0; line->data[i] != '\0'; i++) {
242 if (line->data[i] == '=') {
243 if (isxdigit(line->data[i + 1]) && isxdigit(line->data[i + 2])) {
244 c = hex(line->data[i + 1]) * 16 + hex(line->data[i + 2]);
245 i += 2;
246 addprintable(out, c);
247 } else if (line->data[i + 1] == '\0') {
248 softbreak = 1;
249 break;
250 }
251 } else
252 addprintable(out, line->data[i]);
253 }
254
255 buf_move(line, out);
256 buf_free(out);
257 return (softbreak);
258 }
259
260 int decode_header(BUFFER *in)
261 {
262 int encoded = 0;
263 int c;
264 int err = 0;
265 int last = 0;
266 BUFFER *out;
267
268 out = buf_new();
269 for (in->ptr = 0; in->data[in->ptr] != '\0'; in->ptr++) {
270 if (encoded == 'q' && in->data[in->ptr] == '=' &&
271 isxdigit(in->data[in->ptr + 1])
272 && isxdigit(in->data[in->ptr + 2])) {
273 c = hex(in->data[in->ptr + 1]) * 16 + hex(in->data[in->ptr + 2]);
274 in->ptr += 2;
275 addprintable(out, c);
276 } else if (encoded == 'q' && in->data[in->ptr] == '_')
277 buf_appendc(out, ' ');
278 else if (in->data[in->ptr] == '=' && in->data[in->ptr + 1] == '?' &&
279 in->data[in->ptr + 2] != '\0') {
280 if (last > 0 && out->length > last) {
281 out->data[last] = '\0';
282 out->length = last;
283 }
284 in->ptr++;
285 while (in->data[++in->ptr] != '?')
286 if (in->data[in->ptr] == 0) {
287 err = -1;
288 goto end;
289 }
290 if (in->data[in->ptr + 1] != '\0' && in->data[in->ptr + 2] == '?') {
291 encoded = tolower(in->data[in->ptr + 1]);
292 in->ptr += 2;
293 if (encoded == 'b') {
294 BUFFER *tmp;
295
296 tmp = buf_new();
297 decode(in, tmp);
298 addprintablebuf(out, tmp);
299 last = out->length;
300 buf_free(tmp);
301 } else if (encoded != 'q')
302 err = 1;
303 } else {
304 err = -1;
305 goto end;
306 }
307 } else if (encoded && in->data[in->ptr] == '?' &&
308 in->data[in->ptr + 1] == '=') {
309 in->ptr++;
310 last = out->length;
311 encoded = 0;
312 } else {
313 addprintable(out, in->data[in->ptr]);
314 if (!encoded || !isspace(in->data[in->ptr]))
315 last = out->length;
316 }
317 }
318 end:
319 if (err == -1)
320 buf_set(out, in);
321
322 buf_move(in, out);
323 buf_free(out);
324 return (err);
325 }
326
327 #define delimclose 2
328
329 int boundary(BUFFER *line, BUFFER *boundary)
330 {
331 int c;
332
333 if (boundary->length == 0 || !bufleft(line, "--") ||
334 !strleft(line->data + 2, boundary->data))
335 return (0);
336 line->ptr = boundary->length + 2;
337 for (;;) {
338 c = buf_getc(line);
339 if (c == -1)
340 return (1);
341 if (c == '-' && buf_getc(line) == '-')
342 return (delimclose);
343 if (!isspace(c))
344 return (0);
345 }
346 }
347
348 #define pgpenc 1
349 #define pgpsig 2
350
351 int entity_decode(BUFFER *msg, int message, int mptype, BUFFER *data)
352 {
353 BUFFER *out, *line, *field, *content, *type, *subtype, *disposition,
354 *mboundary, *part, *sigdata;
355 int ret = 0, ptype = 0, partno = 0;
356 int p, encoded = 0;
357
358 out = buf_new();
359 line = buf_new();
360 field = buf_new();
361 content = buf_new();
362 type = buf_new();
363 subtype = buf_new();
364 disposition = buf_new();
365 mboundary = buf_new();
366 part = buf_new();
367 sigdata = buf_new();
368
369 if (message && bufileft(msg, "From ")) {
370 buf_getline(msg, out); /* envelope from */
371 buf_nl(out);
372 }
373
374 while (buf_getheader(msg, field, content) == 0) {
375 if (bufieq(field, "content-transfer-encoding") &&
376 bufieq(content, "quoted-printable"))
377 encoded = 'q';
378 if (bufieq(field, "content-type")) {
379 get_type(content, type, subtype);
380 if (bufieq(type, "multipart"))
381 get_parameter(content, "boundary", mboundary);
382 if (bufieq(type, "multipart") && bufieq(subtype, "encrypted")) {
383 get_parameter(content, "protocol", line);
384 if (bufieq(line, "application/pgp-encrypted"))
385 ptype = pgpenc;
386 }
387 if (bufieq(type, "multipart") && bufieq(subtype, "signed")) {
388 get_parameter(content, "protocol", line);
389 if (bufieq(line, "application/pgp-signature"))
390 ptype = pgpsig;
391 }
392 }
393 if (bufieq(field, "content-disposition"))
394 buf_set(disposition, content);
395 if (message) {
396 decode_header(content);
397 buf_appendheader(out, field, content);
398 }
399 }
400
401 if (message)
402 buf_nl(out);
403
404 if (bufifind(disposition, "attachment")) {
405 buf_appendf(out, "[-- %b attachment", type);
406 get_parameter(disposition, "filename", line);
407 if (line->length)
408 buf_appendf(out, " (%b)", line);
409 buf_appends(out, " --]\n");
410 }
411
412 if (mboundary->length) {
413 /* multipart */
414 while (buf_getline(msg, line) > -1 && !boundary(line, mboundary))
415 ; /* ignore preamble */
416 while (buf_getline(msg, line) != -1) {
417 p = boundary(line, mboundary);
418 if (p) {
419 if (part->data[part->length - 1] == '\n')
420 part->data[--(part->length)] = '\0';
421 partno++;
422 if (ptype == pgpsig && partno == 1)
423 buf_set(sigdata, part);
424 ret += entity_decode(part, 0, ptype, sigdata);
425 buf_cat(out, part);
426 buf_clear(part);
427 if (p == delimclose)
428 break;
429 if (bufieq(subtype, "alternative") && ret > 0)
430 break;
431 if (bufieq(subtype, "mixed"))
432 buf_appends(out,
433 "[-------------------------------------------------------------------------]\n");
434 } else {
435 buf_cat(part, line);
436 buf_nl(part);
437 }
438 }
439 } else if (mptype == pgpenc && bufieq(type, "application") &&
440 bufieq(subtype, "pgp-encrypted")) {
441 /* application/pgp-encrypted part of multipart/encrypted */
442 ; /* skip */
443 } else if (mptype == pgpenc && bufieq(type, "application") &&
444 bufieq(subtype, "octet-stream")) {
445 /* application/octet-stream part of multipart/encrypted */
446 int ok = 0;
447 buf_getline(msg, line);
448 if (bufleft(line, info_beginpgp)) {
449 if (buffind(line, "(SIGNED)")) {
450 buf_getline(msg, line);
451 buf_appends(out, "[-- OpenPGP message with signature --]\n");
452 if (bufleft(line, info_pgpsig))
453 buf_appendf(out, "[%s]\n",
454 line->data + sizeof(info_pgpsig) - 1);
455 else
456 buf_appends(out, "[Signature invalid]\n");
457 } else
458 buf_appends(out, "[-- OpenPGP message --]\n");
459 while (buf_getline(msg, line) != -1) {
460 if (bufleft(line, info_endpgp)) {
461 ret += entity_decode(part, 0, 0, NULL);
462 buf_cat(out, part);
463 buf_appends(out, "[-- End OpenPGP message --]\n");
464 ok = 1, ret++;
465 break;
466 }
467 buf_cat(part, line);
468 buf_nl(part);
469 }
470 }
471 if (!ok) {
472 buf_appends(out, "[-- Bad OpenPGP message --]\n");
473 buf_cat(out, msg);
474 }
475 } else if (mptype == pgpsig && bufeq(type, "application") &&
476 bufieq(subtype, "pgp-signature")) {
477 buf_rest(part, msg);
478 if (pgp_decrypt(part, NULL, data, PGPPUBRING, NULL) == PGP_SIGOK)
479 buf_appendf(out, "[-- OpenPGP signature from:\n %b --]\n", data);
480 else
481 buf_appends(out, "[-- Invalid OpenPGP signature --]\n");
482 } else if (type->length == 0 || bufieq(type, "text")) {
483 while (buf_getline(msg, line) != -1) {
484 int softbreak;
485 softbreak = encoded ? decode_line(line) : 0;
486 buf_cat(out, line);
487 if (!softbreak)
488 buf_nl(out);
489 }
490 ret++;
491 } else {
492 buf_appendf(out, "[-- %b/%b message part --]\n", type, subtype);
493 buf_cat(out, msg);
494 }
495
496 buf_move(msg, out);
497 buf_free(line);
498 buf_free(out);
499 buf_free(field);
500 buf_free(content);
501 buf_free(type);
502 buf_free(subtype);
503 buf_free(disposition);
504 buf_free(mboundary);
505 buf_free(part);
506 buf_free(sigdata);
507 return (0);
508 }
509
510 void mimedecode(BUFFER *msg)
511 {
512 entity_decode(msg, 1, 0, NULL);
513 }
514
515 int attachfile(BUFFER *message, BUFFER *filename)
516 {
517 BUFFER *type, *attachment;
518 FILE *f;
519 int ret = -1;
520
521 type = buf_new();
522 attachment = buf_new();
523
524 if ((bufiright(filename, ".txt") || !bufifind(filename, ".")) &&(strlen(DEFLTENTITY) != 0))
525 buf_sets(type, DEFLTENTITY);
526 else if (bufiright(filename, ".htm") || bufiright(filename, ".html"))
527 buf_sets(type, "text/html");
528 else if (bufiright(filename, ".jpeg"))
529 buf_sets(type, "image/jpeg");
530 else if (bufiright(filename, ".gif"))
531 buf_sets(type, "image/gif");
532 else if (bufiright(filename, ".pcm"))
533 buf_sets(type, "audio/basic");
534 else if (bufiright(filename, ".mpg") || bufiright(filename, ".mpeg"))
535 buf_sets(type, "video/mpeg");
536 else if (bufiright(filename, ".ps"))
537 buf_sets(type, "application/postscript");
538 else
539 buf_sets(type, "application/octet-stream");
540
541 f = fopen(filename->data, "r");
542 if (f) {
543 buf_read(attachment, f);
544 fclose(f);
545 ret = mime_attach(message, attachment, type);
546 }
547
548 buf_free(attachment);
549 buf_free(type);
550 return(ret);
551 }
552
553 int mime_attach(BUFFER *message, BUFFER *attachment, BUFFER *attachtype)
554 {
555 BUFFER *out, *part, *line, *type, *subtype, *mboundary, *field, *content;
556 int mimeheader = 0, multipart = 0, versionheader = 0;
557
558 out = buf_new();
559 line = buf_new();
560 part = buf_new();
561 type = buf_new();
562 subtype = buf_new();
563 mboundary = buf_new();
564 field = buf_new();
565 content = buf_new();
566
567 buf_rewind(message);
568 while (buf_getheader(message, field, content) == 0) {
569 if (bufieq(field, "mime-version"))
570 versionheader = 1;
571 if (bufieq(field, "content-type")) {
572 get_type(content, type, subtype);
573 if (bufieq(type, "multipart") && bufieq(subtype, "mixed")) {
574 multipart = 1;
575 get_parameter(content, "boundary", mboundary);
576 }
577 }
578 if (bufileft(field, "content-"))
579 mimeheader = 1;
580 }
581
582 if (mimeheader && !multipart) {
583 buf_rewind(message);
584 while (buf_getheader(message, field, content) == 0) {
585 if (bufileft(field, "content-"))
586 buf_appendheader(part, field, content);
587 else
588 buf_appendheader(out, field, content);
589 }
590 } else {
591 buf_ungetc(message);
592 buf_append(out, message->data, message->ptr);
593 buf_getc(message);
594 }
595
596 if (!versionheader)
597 buf_appends(out, "MIME-Version: 1.0\n");
598
599 if (!multipart) {
600 buf_setrnd(mboundary, 18);
601 encode(mboundary, 0);
602 buf_appendf(out, "Content-Type: multipart/mixed; boundary=\"%b\"\n",
603 mboundary);
604 }
605 buf_nl(out);
606
607 if (multipart) {
608 while (buf_getline(message, line) != -1) {
609 if (boundary(line, mboundary) == delimclose)
610 break;
611 buf_cat(out, line);
612 buf_nl(out);
613 }
614 } else {
615 buf_appendf(out, "--%b\n", mboundary);
616 if (part->length) {
617 buf_cat(out, part); /* body part header */
618 }
619 else {
620 if (strlen(DEFLTENTITY))
621 buf_appendf(out, "Content-Type: %s\n", DEFLTENTITY);
622 }
623
624 buf_nl(out);
625 buf_cat(out, message);
626 buf_nl(out);
627 }
628
629 buf_appendf(out, "--%b\n", mboundary);
630 buf_appendf(out, "Content-Type: %b\n", attachtype);
631
632 body_encode(attachment, MIME_8BIT, line);
633 buf_cat(out, line);
634 buf_nl(out);
635 buf_cat(out, attachment);
636 buf_appendf(out, "\n--%b--\n", mboundary);
637
638 buf_move(message, out);
639
640 buf_free(out);
641 buf_free(line);
642 buf_free(part);
643 buf_free(type);
644 buf_free(subtype);
645 buf_free(mboundary);
646 buf_free(field);
647 buf_free(content);
648 return (1);
649 }
650
651 static int entity_encode(BUFFER *message, BUFFER *out, BUFFER *messagehdr,
652 int encoding)
653 {
654 BUFFER *field, *content, *mboundary, *part, *line, *line2, *tmp;
655
656 field = buf_new();
657 content = buf_new();
658 mboundary = buf_new();
659 part = buf_new();
660 line = buf_new();
661 line2 = buf_new();
662 tmp = buf_new();
663
664 buf_rewind(message);
665 buf_clear(out);
666 buf_clear(messagehdr);
667
668 while (buf_getheader(message, field, content) == 0) {
669 if (bufileft(field, "content-"))
670 buf_appendheader(out, field, content);
671 else if (messagehdr)
672 buf_appendheader(messagehdr, field, content);
673
674 if (bufieq(field, "content-type")) {
675 get_type(content, line, tmp);
676 if (bufieq(line, "multipart"))
677 get_parameter(content, "boundary", mboundary);
678 }
679 }
680
681 buf_nl(out);
682 if (mboundary->length) {
683 while (buf_getline(message, line) != -1) {
684 buf_cat(out, line);
685 buf_nl(out);
686 if (boundary(line, mboundary))
687 break;
688 }
689 while (buf_getline(message, line) != -1) {
690 if (boundary(line, mboundary)) {
691 entity_encode(part, tmp, line2, encoding);
692 buf_cat(out, line2);
693 buf_cat(out, tmp);
694 buf_cat(out, line);
695 buf_nl(out);
696 buf_clear(part);
697 if (boundary(line, mboundary) == delimclose)
698 break;
699 } else {
700 buf_cat(part, line);
701 buf_nl(part);
702 }
703 }
704 } else
705 buf_rest(out, message);
706 buf_rewind(out);
707 mail_encode(out, encoding);
708
709 buf_free(field);
710 buf_free(content);
711 buf_free(mboundary);
712 buf_free(part);
713 buf_free(line);
714 buf_free(line2);
715 buf_free(tmp);
716 return (1);
717 }
718
719 int pgpmime_sign(BUFFER *message, BUFFER *uid, BUFFER *pass, char *secring)
720 {
721 BUFFER *out, *body, *mboundary, *algo;
722 int err;
723
724 out = buf_new();
725 body = buf_new();
726 mboundary = buf_new();
727 algo = buf_new();
728
729 pgp_signhashalgo(algo, uid, secring, pass);
730
731 entity_encode(message, body, out, MIME_7BIT);
732
733 buf_setrnd(mboundary, 18);
734 encode(mboundary, 0);
735 buf_appendf(out, "Content-Type: multipart/signed; boundary=\"%b\";\n",
736 mboundary);
737 buf_appendf(out,
738 "\tmicalg=pgp-%b; protocol=\"application/pgp-signature\"\n",
739 algo);
740 buf_nl(out);
741
742 buf_appendf(out, "--%b\n", mboundary);
743 buf_cat(out, body);
744 buf_nl(out);
745 buf_appendf(out, "--%b\n", mboundary);
746
747 err = pgp_encrypt(PGP_SIGN | PGP_TEXT | PGP_DETACHEDSIG, body, NULL,
748 uid, pass, NULL, secring);
749
750 buf_appends(out, "Content-Type: application/pgp-signature\n");
751 buf_nl(out);
752 buf_cat(out, body);
753 buf_nl(out);
754 buf_appendf(out, "--%b--\n", mboundary);
755 if (err == 0)
756 buf_move(message, out);
757
758 buf_free(out);
759 buf_free(body);
760 buf_free(mboundary);
761 buf_free(algo);
762 return (err);
763 }

  ViewVC Help
Powered by ViewVC 1.1.5