PageRenderTime 55ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/trunk/src/common/INet/GMime.cpp

#
C++ | 1391 lines | 1152 code | 174 blank | 65 comment | 285 complexity | d10e3a23e790f758f2faace104e7c5ab MD5 | raw file
Possible License(s): LGPL-2.1, Unlicense
  1. #include <stdio.h>
  2. #include "LgiNetInc.h"
  3. #include "Lgi.h"
  4. #include "GMime.h"
  5. #include "GToken.h"
  6. #include "Base64.h"
  7. static const char *MimeEol = "\r\n";
  8. static const char *MimeWs = " \t\r\n";
  9. static const char *MimeStr = "\'\"";
  10. static const char *MimeQuotedPrintable = "quoted-printable";
  11. static const char *MimeBase64 = "base64";
  12. #define MimeMagic ( ('M'<<24) | ('I'<<24) | ('M'<<24) | ('E'<<24) )
  13. #define SkipWs(s) while (*s && strchr(MimeWs, *s)) s++
  14. #define SkipNonWs(s) while (*s && !strchr(MimeWs, *s)) s++
  15. const char *GMime::DefaultCharset = "text/plain";
  16. ///////////////////////////////////////////////////////////////////////
  17. enum MimeBoundary
  18. {
  19. MimeData = 0,
  20. MimeNextSeg = 1,
  21. MimeEndSeg = 2
  22. };
  23. MimeBoundary IsMimeBoundary(char *Boundary, char *Line)
  24. {
  25. if (Boundary)
  26. {
  27. int BoundaryLen = strlen(Boundary);
  28. if (Line &&
  29. *Line++ == '-' &&
  30. *Line++ == '-' &&
  31. strncmp(Line, Boundary, strlen(Boundary)) == 0)
  32. {
  33. // MIME segment boundary
  34. Line += BoundaryLen;
  35. if (Line[0] == '-' &&
  36. Line[1] == '-')
  37. {
  38. return MimeEndSeg;
  39. }
  40. else
  41. {
  42. return MimeNextSeg;
  43. }
  44. }
  45. }
  46. return MimeData;
  47. }
  48. void CreateMimeBoundary(char *Buf)
  49. {
  50. if (Buf)
  51. {
  52. static int Count = 1;
  53. sprintf(Buf, "--%x-%x-%x--", (int)LgiCurrentTime(), (int)LgiGetCurrentThread(), Count++);
  54. }
  55. }
  56. ///////////////////////////////////////////////////////////////////////
  57. class GCoderStream : public GStream
  58. {
  59. protected:
  60. GStreamI *Out;
  61. public:
  62. GCoderStream(GStreamI *o)
  63. {
  64. Out = o;
  65. }
  66. };
  67. class GMimeQuotedPrintableEncode : public GCoderStream
  68. {
  69. char *d;
  70. char Buf[256];
  71. public:
  72. GMimeQuotedPrintableEncode(GStreamI *o) : GCoderStream(o)
  73. {
  74. Buf[0] = 0;
  75. d = Buf;
  76. }
  77. int Write(const void *p, int size, int f = 0)
  78. {
  79. char *s = (char*)p;
  80. char *e = s + size;
  81. while (s < e)
  82. {
  83. if (*s == '\n' || !*s)
  84. {
  85. *d++ = '\r';
  86. *d++ = '\n';
  87. int Len = d-Buf;
  88. if (Out->Write(Buf, Len) < Len ||
  89. !*s)
  90. {
  91. break;
  92. }
  93. d = Buf;
  94. s++;
  95. }
  96. else if (*s & 0x80 ||
  97. *s == '.' ||
  98. *s == '=')
  99. {
  100. sprintf(d, "=%2.2X", (uchar)*s);
  101. d += 3;
  102. s++;
  103. }
  104. else if (*s != '\r')
  105. {
  106. *d++ = *s++;
  107. }
  108. else
  109. {
  110. s++;
  111. }
  112. if (d-Buf > 73)
  113. {
  114. // time for a new line.
  115. strcpy(d, "=\r\n");
  116. d += 3;
  117. int Len = d-Buf;
  118. if (Out->Write(Buf, Len) < Len)
  119. {
  120. break;
  121. }
  122. d = Buf;
  123. }
  124. }
  125. return s - (const char*)p;
  126. }
  127. };
  128. class GMimeQuotedPrintableDecode : public GCoderStream
  129. {
  130. GStringPipe Buf;
  131. char ConvHexToBin(char c)
  132. {
  133. if (c >= '0' && c <= '9')
  134. return c - '0';
  135. if (c >= 'a' && c <= 'f')
  136. return c + 10 - 'a';
  137. if (c >= 'A' && c <= 'F')
  138. return c + 10 - 'A';
  139. return 0;
  140. }
  141. public:
  142. GMimeQuotedPrintableDecode(GStreamI *o) : GCoderStream(o) {}
  143. int Write(const void *p, int size, int f = 0)
  144. {
  145. Buf.Push((char*)p, size);
  146. char In[512];
  147. char Line[512];
  148. char *o = Line;
  149. while (Buf.Pop(In, sizeof(In)))
  150. {
  151. for (char *s=In; *s; )
  152. {
  153. if (*s == '=')
  154. {
  155. s++; // skip '='
  156. if (*s == '\r' || *s == '\n')
  157. {
  158. if (*s == '\r')
  159. s++;
  160. if (*s == '\n')
  161. s++;
  162. }
  163. else if (*s)
  164. {
  165. char c = ConvHexToBin(*s++);
  166. *o++ = (c << 4) | (ConvHexToBin(*s++) & 0xF);
  167. }
  168. else break;
  169. }
  170. else
  171. {
  172. *o++ = *s++;
  173. }
  174. }
  175. int Len = o-Line;
  176. if (Out->Write(Line, Len) < Len)
  177. {
  178. // Error
  179. return 0;
  180. }
  181. o = Line;
  182. }
  183. return size;
  184. }
  185. };
  186. class GMimeBase64Encode : public GCoderStream
  187. {
  188. GBytePipe Buf;
  189. public:
  190. GMimeBase64Encode(GStreamI *o) : GCoderStream(o) {}
  191. ~GMimeBase64Encode()
  192. {
  193. uchar b[100];
  194. int Len = Buf.GetSize();
  195. LgiAssert(Len < sizeof(b));
  196. int r = Buf.Read(b, Len);
  197. if (r)
  198. {
  199. char t[256];
  200. int w = ConvertBinaryToBase64(t, sizeof(t), b, r);
  201. Out->Write(t, w);
  202. Out->Write(MimeEol, 2);
  203. }
  204. }
  205. int Write(const void *p, int size, int f = 0)
  206. {
  207. Buf.Write((uchar*)p, size);
  208. while (Buf.GetSize() >= 72)
  209. {
  210. uchar b[100];
  211. int r = Buf.Read(b, 72);
  212. if (r)
  213. {
  214. char t[256];
  215. int w = ConvertBinaryToBase64(t, sizeof(t), b, r);
  216. Out->Write(t, w);
  217. Out->Write(MimeEol, 2);
  218. }
  219. else return 0;
  220. }
  221. return size;
  222. }
  223. };
  224. class GMimeBase64Decode : public GCoderStream
  225. {
  226. GStringPipe Buf;
  227. uint8 Lut[256];
  228. public:
  229. GMimeBase64Decode(GStreamI *o) : GCoderStream(o)
  230. {
  231. ZeroObj(Lut);
  232. memset(Lut+'a', 1, 'z'-'a'+1);
  233. memset(Lut+'A', 1, 'Z'-'A'+1);
  234. memset(Lut+'0', 1, '9'-'0'+1);
  235. Lut['+'] = 1;
  236. Lut['/'] = 1;
  237. Lut['='] = 1;
  238. }
  239. int Write(const void *p, int size, int f = 0)
  240. {
  241. int Status = 0;
  242. // Push non whitespace into the memory buf
  243. char *s = (char*)p;
  244. char *e = s + size;
  245. while (s < e)
  246. {
  247. while (*s && s < e && !Lut[*s]) s++;
  248. char *Start = s;
  249. while (*s && s < e && Lut[*s]) s++;
  250. Buf.Push(Start, s-Start);
  251. }
  252. // While there is at least one run of base64 (4 bytes) convert it to text
  253. // and write it to the output stream
  254. int Size;
  255. while ((Size = Buf.GetSize()) > 3)
  256. {
  257. Size &= ~3;
  258. char t[256];
  259. int r = min(sizeof(t), Size);
  260. if ((r = Buf.GBytePipe::Read((uchar*)t, r)) > 0)
  261. {
  262. uchar b[256];
  263. int w = ConvertBase64ToBinary(b, sizeof(b), t, r);
  264. Out->Write(b, w);
  265. Status += w;
  266. }
  267. }
  268. return Status;
  269. }
  270. };
  271. ///////////////////////////////////////////////////////////////////////
  272. GMimeBuf::GMimeBuf(GStreamI *src, GStreamEnd *end)
  273. {
  274. Total = 0;
  275. Src = src;
  276. End = end;
  277. Src->SetPos(0);
  278. }
  279. int GMimeBuf::Pop(char *Str, int BufSize)
  280. {
  281. int Ret = 0;
  282. while (!(Ret = GStringPipe::Pop(Str, BufSize)))
  283. {
  284. if (Src)
  285. {
  286. char Buf[1024];
  287. int r = Src ? Src->Read(Buf, sizeof(Buf)) : 0;
  288. if (r)
  289. {
  290. if (End)
  291. {
  292. int e = End->IsEnd(Buf, r);
  293. if (e >= 0)
  294. {
  295. // End of stream
  296. int s = e - Total;
  297. Push(Buf, s);
  298. Total += s;
  299. Src = 0; // no more data anyway
  300. }
  301. else
  302. {
  303. // Not the end
  304. Push(Buf, r);
  305. Total += r;
  306. }
  307. }
  308. else
  309. {
  310. Push(Buf, r);
  311. Total += r;
  312. }
  313. }
  314. else break;
  315. }
  316. else break;
  317. }
  318. return Ret;
  319. }
  320. ///////////////////////////////////////////////////////////////////////
  321. // Mime Object
  322. GMime::GMime(char *tmp)
  323. {
  324. Parent = 0;
  325. TmpPath = NewStr(tmp);
  326. Headers = 0;
  327. DataPos = 0;
  328. DataSize = 0;
  329. DataLock = 0;
  330. DataStore = 0;
  331. OwnDataStore = 0;
  332. Text.Decode.Mime = this;
  333. Text.Encode.Mime = this;
  334. Binary.Read.Mime = this;
  335. Binary.Write.Mime = this;
  336. }
  337. GMime::~GMime()
  338. {
  339. Remove();
  340. Empty();
  341. DeleteArray(TmpPath);
  342. }
  343. char *GMime::GetFileName()
  344. {
  345. char *n = GetSub("Content-Type", "Name");
  346. if (!n)
  347. n = GetSub("Content-Disposition", "Filename");
  348. if (!n)
  349. {
  350. if (n = Get("Content-Location"))
  351. {
  352. char *trim = TrimStr(n, "\'\"");
  353. if (trim)
  354. {
  355. DeleteArray(n);
  356. n = trim;
  357. }
  358. }
  359. }
  360. return n;
  361. }
  362. bool GMime::Insert(GMime *m, int Pos)
  363. {
  364. LgiAssert(m);
  365. if (!m)
  366. return false;
  367. if (m->Parent)
  368. {
  369. LgiAssert(m->Parent->Children.HasItem(m));
  370. m->Parent->Children.Delete(m, true);
  371. }
  372. m->Parent = this;
  373. LgiAssert(!Children.HasItem(m));
  374. if (Pos >= 0)
  375. Children.AddAt(Pos, m);
  376. else
  377. Children.Add(m);
  378. return true;
  379. }
  380. void GMime::Remove()
  381. {
  382. if (Parent)
  383. {
  384. LgiAssert(Parent->Children.HasItem(this));
  385. Parent->Children.Delete(this, true);
  386. Parent = 0;
  387. }
  388. }
  389. GMime *GMime::operator[](int i)
  390. {
  391. if (i < 0 || i >= Children.Length())
  392. return 0;
  393. return Children[i];
  394. }
  395. char *GMime::GetTmpPath()
  396. {
  397. for (GMime *m = this; m; m = m->Parent)
  398. {
  399. if (m->TmpPath)
  400. return m->TmpPath;
  401. }
  402. return 0;
  403. }
  404. bool GMime::SetHeaders(const char *h)
  405. {
  406. DeleteArray(Headers);
  407. Headers = NewStr(h);
  408. return Headers != 0;
  409. }
  410. bool GMime::Lock()
  411. {
  412. bool Lock = true;
  413. if (DataLock)
  414. {
  415. Lock = DataLock->Lock(_FL);
  416. }
  417. return Lock;
  418. }
  419. void GMime::Unlock()
  420. {
  421. if (DataLock)
  422. {
  423. DataLock->Unlock();
  424. }
  425. }
  426. bool GMime::CreateTempData()
  427. {
  428. bool Status = false;
  429. DataPos = 0;
  430. DataSize = 0;
  431. OwnDataStore = true;
  432. if (DataStore = new GTempStream(GetTmpPath(), 4 << 20))
  433. {
  434. Status = true;
  435. }
  436. return Status;
  437. }
  438. GStreamI *GMime::GetData(bool Detach)
  439. {
  440. GStreamI *Ds = DataStore;
  441. if (Ds)
  442. {
  443. Ds->SetPos(DataPos);
  444. if (Detach)
  445. DataStore = 0;
  446. }
  447. return Ds;
  448. }
  449. bool GMime::SetData(bool OwnStream, GStreamI *d, int Pos, int Size, GSemaphore *l)
  450. {
  451. if (DataStore && Lock())
  452. {
  453. DeleteObj(DataStore);
  454. Unlock();
  455. }
  456. if (d)
  457. {
  458. OwnDataStore = OwnStream;
  459. DataPos = Pos;
  460. DataSize = Size >= 0 ? Size : d->GetSize();
  461. DataLock = l;
  462. DataStore = d;
  463. }
  464. return true;
  465. }
  466. bool GMime::SetData(char *Str, int Len)
  467. {
  468. if (DataStore && Lock())
  469. {
  470. DeleteObj(DataStore);
  471. Unlock();
  472. }
  473. if (Str)
  474. {
  475. if (Len < 0)
  476. {
  477. Len = strlen(Str);
  478. }
  479. DataLock = 0;
  480. DataPos = 0;
  481. DataSize = Len;
  482. DataStore = new GTempStream(GetTmpPath(), 4 << 20);
  483. if (DataStore)
  484. {
  485. DataStore->Write(Str, Len);
  486. }
  487. }
  488. return true;
  489. }
  490. void GMime::Empty()
  491. {
  492. if (OwnDataStore)
  493. {
  494. if (Lock())
  495. {
  496. DeleteObj(DataStore);
  497. Unlock();
  498. }
  499. }
  500. while (Children.Length())
  501. delete Children[0];
  502. DeleteArray(Headers);
  503. DataPos = 0;
  504. DataSize = 0;
  505. DataLock = 0;
  506. DataStore = 0;
  507. OwnDataStore = 0;
  508. }
  509. char *GMime::NewValue(char *&s, bool Alloc)
  510. {
  511. char *Status = 0;
  512. int Inc = 0;
  513. char *End;
  514. if (strchr(MimeStr, *s))
  515. {
  516. // Delimited string
  517. char Delim = *s++;
  518. End = strchr(s, Delim);
  519. Inc = 1;
  520. }
  521. else
  522. {
  523. // Raw string
  524. End = s;
  525. while (*End && *End != ';' && *End != '\n' && *End != '\r') End++;
  526. while (strchr(MimeWs, End[-1])) End--;
  527. }
  528. if (End)
  529. {
  530. if (Alloc)
  531. {
  532. Status = NewStr(s, End-s);
  533. }
  534. s = End + Inc;
  535. }
  536. SkipWs(s);
  537. return Status;
  538. }
  539. char *GMime::StartOfField(char *s, const char *Field)
  540. {
  541. char *Status = 0;
  542. if (s && Field)
  543. {
  544. int FieldLen = strlen(Field);
  545. while (s && *s)
  546. {
  547. if (strchr(MimeWs, *s))
  548. {
  549. s = strchr(s, '\n');
  550. if (s) s++;
  551. }
  552. else
  553. {
  554. char *f = s;
  555. while (*s && *s != ':' && !strchr(MimeWs, *s)) s++;
  556. int fLen = s-f;
  557. if (*s++ == ':' &&
  558. fLen == FieldLen &&
  559. strnicmp(f, Field, FieldLen) == 0)
  560. {
  561. return f;
  562. break;
  563. }
  564. else
  565. {
  566. s = strchr(s, '\n');
  567. if (s) s++;
  568. }
  569. }
  570. }
  571. }
  572. return 0;
  573. }
  574. char *GMime::NextField(char *s)
  575. {
  576. while (s)
  577. {
  578. while (*s && *s != '\n') s++;
  579. if (*s == '\n')
  580. {
  581. s++;
  582. if (!strchr(MimeWs, *s))
  583. {
  584. break;
  585. }
  586. }
  587. else
  588. {
  589. break;
  590. }
  591. }
  592. return s;
  593. }
  594. char *GMime::Get(const char *Name, bool Short, const char *Default)
  595. {
  596. char *Status = 0;
  597. if (Name && Headers)
  598. {
  599. char *s = StartOfField(Headers, Name);
  600. if (s)
  601. {
  602. s = strchr(s, ':');
  603. if (s)
  604. {
  605. s++;
  606. SkipWs(s);
  607. if (Short)
  608. {
  609. Status = NewValue(s);
  610. }
  611. else
  612. {
  613. char *e = NextField(s);
  614. while (strchr(MimeWs, e[-1])) e--;
  615. Status = NewStr(s, e-s);
  616. }
  617. }
  618. }
  619. if (!Status && Default)
  620. {
  621. Status = NewStr(Default);
  622. }
  623. }
  624. return Status;
  625. }
  626. bool GMime::Set(const char *Name, const char *Value)
  627. {
  628. bool Status = false;
  629. if (Name)
  630. {
  631. GStringPipe p;
  632. char *h = Headers;
  633. if (h)
  634. {
  635. char *f = StartOfField(h, Name);
  636. if (f)
  637. {
  638. // 'Name' exists, push out pre 'Name' header text
  639. p.Push(h, f-Headers);
  640. h = NextField(f);
  641. }
  642. else
  643. {
  644. if (!Value)
  645. {
  646. // Nothing to do here...
  647. return true;
  648. }
  649. // 'Name' doesn't exist, push out all the headers
  650. p.Push(Headers);
  651. h = 0;
  652. }
  653. }
  654. if (Value)
  655. {
  656. // Push new field
  657. int Vlen = strlen(Value);
  658. while (Vlen > 0 && strchr(MimeWs, Value[Vlen-1])) Vlen--;
  659. p.Push(Name);
  660. p.Push(": ");
  661. p.Push(Value, Vlen);
  662. p.Push(MimeEol);
  663. }
  664. // else we're deleting the feild
  665. if (h)
  666. {
  667. // Push out any header text post the 'Name' field.
  668. p.Push(h);
  669. }
  670. DeleteArray(Headers);
  671. Headers = (char*)p.New(sizeof(char));
  672. }
  673. return Status;
  674. }
  675. char *GMime::GetSub(const char *Field, const char *Sub)
  676. {
  677. char *Status = 0;
  678. if (Field && Sub)
  679. {
  680. int SubLen = strlen(Sub);
  681. char *v = Get(Field, false);
  682. if (v)
  683. {
  684. // Move past the field value into the sub fields
  685. char *s = v;
  686. SkipWs(s);
  687. while (*s && *s != ';' && !strchr(MimeWs, *s)) s++;
  688. SkipWs(s);
  689. while (s && *s++ == ';')
  690. {
  691. // Parse each name=value pair
  692. SkipWs(s);
  693. char *Name = s;
  694. while (*s && *s != '=' && !strchr(MimeWs, *s)) s++;
  695. int NameLen = s-Name;
  696. SkipWs(s);
  697. if (*s++ == '=')
  698. {
  699. bool Found = SubLen == NameLen && strnicmp(Name, Sub, NameLen) == 0;
  700. Status = NewValue(s, Found);
  701. if (Found) break;
  702. }
  703. else break;
  704. }
  705. DeleteArray(v);
  706. }
  707. }
  708. return Status;
  709. }
  710. bool GMime::SetSub(const char *Field, const char *Sub, const char *Value, const char *DefaultValue)
  711. {
  712. if (Field && Sub)
  713. {
  714. char Buf[256];
  715. char *s = StartOfField(Headers, Field);
  716. if (s)
  717. {
  718. // Header already exists
  719. s = strchr(s, ':');
  720. if (s++)
  721. {
  722. SkipWs(s);
  723. GStringPipe p;
  724. // Push the field data
  725. char *e = s;
  726. while (*e && !strchr("; \t\r\n", *e)) e++;
  727. p.Push(s, e-s);
  728. SkipWs(e);
  729. // Loop through the subfields and push all those that are not 'Sub'
  730. s = e;
  731. while (*s++ == ';')
  732. {
  733. SkipWs(s);
  734. char *e = s;
  735. while (*e && *e != '=' && !strchr(MimeWs, *e)) e++;
  736. char *Name = NewStr(s, e-s);
  737. if (Name)
  738. {
  739. s = e;
  740. SkipWs(s);
  741. if (*s++ == '=')
  742. {
  743. char *v = NewValue(s);
  744. if (stricmp(Name, Sub) != 0)
  745. {
  746. sprintf(Buf, ";\r\n\t%s=\"%s\"", Name, v);
  747. p.Push(Buf);
  748. }
  749. DeleteArray(v);
  750. }
  751. else break;
  752. }
  753. else break;
  754. }
  755. if (Value)
  756. {
  757. // Push the new sub field
  758. sprintf(Buf, ";\r\n\t%s=\"%s\"", Sub, Value);
  759. p.Push(Buf);
  760. }
  761. char *Data = p.NewStr();
  762. if (Data)
  763. {
  764. Set(Field, Data);
  765. DeleteArray(Data);
  766. }
  767. }
  768. }
  769. else if (DefaultValue)
  770. {
  771. // Header doesn't exist at all
  772. if (Value)
  773. {
  774. // Set
  775. sprintf(Buf, "%s;\r\n\t%s=\"%s\"", DefaultValue, Sub, Value);
  776. return Set(Field, Buf);
  777. }
  778. else
  779. {
  780. // Remove
  781. return Set(Field, DefaultValue);
  782. }
  783. }
  784. }
  785. return false;
  786. }
  787. /////////////////////////////////////////////////////////////////////////
  788. // Mime Text Conversion
  789. // Rfc822 Text -> Object
  790. int GMime::GMimeText::GMimeDecode::Pull(GStreamI *Source, GStreamEnd *End)
  791. {
  792. GMimeBuf Buf(Source, End); // Stream -> Lines
  793. return Parse(&Buf);
  794. }
  795. class ParentState
  796. {
  797. public:
  798. char *Boundary;
  799. MimeBoundary Type;
  800. ParentState()
  801. {
  802. Boundary = 0;
  803. Type = MimeData;
  804. }
  805. };
  806. int GMime::GMimeText::GMimeDecode::Parse(GStringPipe *Source, ParentState *State)
  807. {
  808. int Status = 0;
  809. if (Mime && Source)
  810. {
  811. Mime->Empty();
  812. if (!Mime->CreateTempData())
  813. LgiAssert(!"CreateTempData failed.");
  814. else
  815. {
  816. // Read the headers..
  817. char Buf[1024];
  818. GStringPipe HeaderBuf;
  819. int r;
  820. while ((r = Source->Pop(Buf, sizeof(Buf))) > 0)
  821. {
  822. if (!strchr(MimeEol, Buf[0]))
  823. {
  824. // Store part of the headers
  825. HeaderBuf.Push(Buf, r);
  826. }
  827. else break;
  828. }
  829. if (r < 0)
  830. return 0;
  831. // Not an error
  832. Mime->Headers = HeaderBuf.NewStr();
  833. // Get various bits out of the header
  834. char *Encoding = Mime->GetEncoding();
  835. char *Boundary = Mime->GetBoundary();
  836. int BoundaryLen = Boundary ? strlen(Boundary) : 0;
  837. GStream *Decoder = 0;
  838. if (Encoding)
  839. {
  840. if (stricmp(Encoding, MimeQuotedPrintable) == 0)
  841. {
  842. Decoder = new GMimeQuotedPrintableDecode(Mime->DataStore);
  843. }
  844. else if (stricmp(Encoding, MimeBase64) == 0)
  845. {
  846. Decoder = new GMimeBase64Decode(Mime->DataStore);
  847. }
  848. }
  849. DeleteArray(Encoding);
  850. // Read in the rest of the MIME segment
  851. bool Done = false;
  852. int StartPos = Mime->DataStore->GetPos();
  853. while (!Done)
  854. {
  855. // Process existing lines
  856. int Len;
  857. int Written = 0;
  858. Status = true;
  859. while (Len = Source->Pop(Buf, sizeof(Buf)))
  860. {
  861. // Check for boundary
  862. MimeBoundary Type = MimeData;
  863. if (Boundary)
  864. {
  865. Type = IsMimeBoundary(Boundary, Buf);
  866. }
  867. if (State)
  868. {
  869. State->Type = IsMimeBoundary(State->Boundary, Buf);
  870. if (State->Type)
  871. {
  872. Status = Done = true;
  873. break;
  874. }
  875. }
  876. DoSegment:
  877. if (Type == MimeNextSeg)
  878. {
  879. ParentState MyState;
  880. MyState.Boundary = Boundary;
  881. GMime *Seg = new GMime(Mime->GetTmpPath());
  882. if (Seg &&
  883. Seg->Text.Decode.Parse(Source, &MyState))
  884. {
  885. Mime->Insert(Seg);
  886. if (MyState.Type)
  887. {
  888. Type = MyState.Type;
  889. goto DoSegment;
  890. }
  891. }
  892. else break;
  893. }
  894. else if (Type == MimeEndSeg)
  895. {
  896. Done = true;
  897. break;
  898. }
  899. else
  900. {
  901. // Process data
  902. if (Decoder)
  903. {
  904. Written += Decoder->Write(Buf, Len);
  905. }
  906. else
  907. {
  908. int w = Mime->DataStore->Write(Buf, Len);
  909. if (w > 0)
  910. {
  911. Written += w;
  912. }
  913. else
  914. {
  915. Done = true;
  916. Status = false;
  917. break;
  918. }
  919. }
  920. }
  921. }
  922. Mime->DataSize = Written;
  923. if (Len == 0)
  924. {
  925. Done = true;
  926. }
  927. }
  928. DeleteObj(Decoder);
  929. DeleteArray(Boundary);
  930. }
  931. }
  932. return Status;
  933. }
  934. void GMime::GMimeText::GMimeDecode::Empty()
  935. {
  936. }
  937. // Object -> Rfc822 Text
  938. int GMime::GMimeText::GMimeEncode::Push(GStreamI *Dest, GStreamEnd *End)
  939. {
  940. int Status = 0;
  941. if (Mime)
  942. {
  943. char Buf[1024];
  944. // Check boundary
  945. char *Boundary = Mime->GetBoundary();
  946. if (Mime->Children.Length())
  947. {
  948. // Boundary required
  949. if (!Boundary)
  950. {
  951. // Create one
  952. char b[256];
  953. CreateMimeBoundary(b);
  954. Mime->SetBoundary(b);
  955. Boundary = Mime->GetBoundary();
  956. }
  957. }
  958. else if (Boundary)
  959. {
  960. // Remove boundary
  961. Mime->SetBoundary(0);
  962. DeleteArray(Boundary);
  963. }
  964. // Check encoding
  965. char *Encoding = Mime->GetEncoding();
  966. if (!Encoding)
  967. {
  968. // Detect an appropriate encoding
  969. int MaxLine = 0;
  970. bool Has8Bit = false;
  971. bool HasBin = false;
  972. if (Mime->DataStore &&
  973. Mime->Lock())
  974. {
  975. Mime->DataStore->SetPos(Mime->DataPos);
  976. int x = 0;
  977. for (int i=0; i<Mime->DataSize; )
  978. {
  979. int m = min(Mime->DataSize - i, sizeof(Buf));
  980. int r = Mime->DataStore->Read(Buf, m);
  981. if (r > 0)
  982. {
  983. for (int n=0; n<r; n++)
  984. {
  985. if (Buf[n] == '\n')
  986. {
  987. MaxLine = max(x, MaxLine);
  988. x = 0;
  989. }
  990. else
  991. {
  992. x++;
  993. }
  994. if (Buf[n] & 0x80)
  995. {
  996. Has8Bit = true;
  997. }
  998. if (Buf[n] < ' ' &&
  999. Buf[n] != '\t' &&
  1000. Buf[n] != '\r' &&
  1001. Buf[n] != '\n')
  1002. {
  1003. HasBin = true;
  1004. }
  1005. }
  1006. i += r;
  1007. }
  1008. else break;
  1009. }
  1010. Mime->Unlock();
  1011. }
  1012. if (HasBin)
  1013. {
  1014. Encoding = NewStr(MimeBase64);
  1015. }
  1016. else if (Has8Bit || MaxLine > 70)
  1017. {
  1018. Encoding = NewStr(MimeQuotedPrintable);
  1019. }
  1020. if (Encoding)
  1021. {
  1022. Mime->SetEncoding(Encoding);
  1023. }
  1024. }
  1025. // Write the headers
  1026. GToken h(Mime->Headers, MimeEol);
  1027. for (int i=0; i<h.Length(); i++)
  1028. {
  1029. Dest->Write(h[i], strlen(h[i]));
  1030. Dest->Write(MimeEol, 2);
  1031. }
  1032. Dest->Write(MimeEol, 2);
  1033. // Write data
  1034. GStream *Encoder = 0;
  1035. if (Encoding)
  1036. {
  1037. if (stricmp(Encoding, MimeQuotedPrintable) == 0)
  1038. {
  1039. Encoder = new GMimeQuotedPrintableEncode(Dest);
  1040. }
  1041. else if (stricmp(Encoding, MimeBase64) == 0)
  1042. {
  1043. Encoder = new GMimeBase64Encode(Dest);
  1044. }
  1045. }
  1046. if (Mime->DataStore)
  1047. {
  1048. if (Mime->Lock())
  1049. {
  1050. Mime->DataStore->SetPos(Mime->DataPos);
  1051. Status = Mime->DataSize == 0; // Nothing is a valid segment??
  1052. for (int i=0; i<Mime->DataSize; )
  1053. {
  1054. int m = min(Mime->DataSize-i, sizeof(Buf));
  1055. int r = Mime->DataStore->Read(Buf, m);
  1056. if (r > 0)
  1057. {
  1058. if (Encoder)
  1059. {
  1060. Encoder->Write(Buf, r);
  1061. }
  1062. else
  1063. {
  1064. Dest->Write(Buf, r);
  1065. }
  1066. Status = true;
  1067. }
  1068. else break;
  1069. }
  1070. Mime->Unlock();
  1071. }
  1072. }
  1073. else
  1074. {
  1075. Status = true;
  1076. }
  1077. DeleteObj(Encoder);
  1078. // Write children
  1079. if (Mime->Children.Length() && Boundary)
  1080. {
  1081. for (int i=0; i<Mime->Children.Length(); i++)
  1082. {
  1083. sprintf(Buf, "\r\n\r\n--%s\r\n", Boundary);
  1084. Dest->Write(Buf, strlen(Buf));
  1085. if (!Mime->Children[i]->Text.Encode.Push(Dest, End))
  1086. {
  1087. break;
  1088. }
  1089. Status = 1;
  1090. }
  1091. sprintf(Buf, "\r\n\r\n--%s--\r\n", Boundary);
  1092. Dest->Write(Buf, strlen(Buf));
  1093. }
  1094. // Clean up
  1095. DeleteArray(Encoding);
  1096. DeleteArray(Boundary);
  1097. }
  1098. return Status;
  1099. }
  1100. void GMime::GMimeText::GMimeEncode::Empty()
  1101. {
  1102. }
  1103. /////////////////////////////////////////////////////////////////////////
  1104. // Mime Binary Serialization
  1105. // Source -> Object
  1106. int GMime::GMimeBinary::GMimeRead::Pull(GStreamI *Source, GStreamEnd *End)
  1107. {
  1108. if (Source)
  1109. {
  1110. int32 Header[4];
  1111. Mime->Empty();
  1112. // Read header block (Magic, HeaderSize, DataSize, # of Children)
  1113. // and check magic
  1114. if (Source->Read(Header, sizeof(Header)) == sizeof(Header) &&
  1115. Header[0] == MimeMagic)
  1116. {
  1117. // Read header data
  1118. Mime->Headers = new char[Header[1]+1];
  1119. if (Mime->Headers &&
  1120. Source->Read(Mime->Headers, Header[1]) == Header[1])
  1121. {
  1122. // NUL terminate
  1123. Mime->Headers[Header[1]] = 0;
  1124. // Skip body data
  1125. if (Source->SetPos(Source->GetPos() + Header[2]) > 0)
  1126. {
  1127. // Read the children in
  1128. for (int i=0; i<Header[3]; i++)
  1129. {
  1130. GMime *c = new GMime(Mime->GetTmpPath());
  1131. if (c &&
  1132. c->Binary.Read.Pull(Source, End))
  1133. {
  1134. Mime->Insert(c);
  1135. }
  1136. else break;
  1137. }
  1138. }
  1139. }
  1140. return 1; // success
  1141. }
  1142. }
  1143. return 0; // failure
  1144. }
  1145. void GMime::GMimeBinary::GMimeRead::Empty()
  1146. {
  1147. }
  1148. // Object -> Dest
  1149. int64 GMime::GMimeBinary::GMimeWrite::GetSize()
  1150. {
  1151. int Size = 0;
  1152. if (Mime)
  1153. {
  1154. Size = (sizeof(int32) * 4) + // Header magic + block sizes
  1155. (Mime->Headers ? strlen(Mime->Headers) : 0) + // Headers
  1156. (Mime->DataStore ? Mime->DataSize : 0); // Data
  1157. // Children
  1158. for (int i=0; i<Mime->Children.Length(); i++)
  1159. {
  1160. Size += Mime->Children[i]->Binary.Write.GetSize();
  1161. }
  1162. }
  1163. return Size;
  1164. }
  1165. int GMime::GMimeBinary::GMimeWrite::Push(GStreamI *Dest, GStreamEnd *End)
  1166. {
  1167. if (Dest && Mime)
  1168. {
  1169. int32 Header[4] =
  1170. {
  1171. MimeMagic,
  1172. Mime->Headers ? strlen(Mime->Headers) : 0,
  1173. Mime->DataStore ? Mime->DataSize : 0,
  1174. Mime->Children.Length()
  1175. };
  1176. if (Dest->Write(Header, sizeof(Header)) == sizeof(Header))
  1177. {
  1178. if (Mime->Headers)
  1179. {
  1180. Dest->Write(Mime->Headers, Header[1]);
  1181. }
  1182. if (Mime->DataStore)
  1183. {
  1184. char Buf[1024];
  1185. int Written = 0;
  1186. int Read = 0;
  1187. int r;
  1188. while ((r = Mime->DataStore->Read(Buf, min(sizeof(Buf), Header[2]-Read) )) > 0)
  1189. {
  1190. int w;
  1191. if ((w = Dest->Write(Buf, r)) <= 0)
  1192. {
  1193. // Write error
  1194. break;
  1195. }
  1196. Written += w;
  1197. Read += r;
  1198. }
  1199. // Check we've written out all the data
  1200. LgiAssert(Written < Header[2]);
  1201. if (Written < Header[2])
  1202. {
  1203. return 0;
  1204. }
  1205. }
  1206. for (int i=0; i<Mime->Children.Length(); i++)
  1207. {
  1208. Mime->Children[i]->Binary.Write.Push(Dest, End);
  1209. }
  1210. return 1;
  1211. }
  1212. }
  1213. return 0;
  1214. }
  1215. void GMime::GMimeBinary::GMimeWrite::Empty()
  1216. {
  1217. }