1 using System;
2 using System.IO;
3 using System.Collections.Generic;
4 using System.Text;
5
6 namespace ReadCookie
7 {
8 public struct O4Cookie
20 {
21 public string Name;
22 public string Value;
23 public string Comment;
24 public string CommentURL;
25 public string RecvDomain;
26 public string RecvPath;
27 public string PortList;
28 public byte Version;
29 public bool Authenticate;
30 public bool Server;
31 public bool Secure;
32 public bool Protected;
33 public bool ThirdParty;
34 public bool Password;
35 public bool Prefixed;
36 public ulong Expires;
37 public ulong LastUsed;
38 }
39
40
76 public class OpraCookieJar
77 {
78
79 private byte[] _RawBytes;
80 private O4Domain _Root;
81
82 #region Opera cookie format elements
83
84
86 const uint O4_VER_FILE = 0x00001000;
88 const uint O4_VER_APP = 0x00002001;
89
90
92 private static O4CookieHeader CookieFileHeader;
94
95
97
99
100 const byte O4_ID_DMN = 0x01;
101
102
103 const byte O4_ID_DMN_NAME = 0x1E;
104
105
106
107
108
109
110 const byte O4_ID_DMN_FILTER = 0x1F;
111
112
113
114
115 const byte O4_ID_DMN_MATCH = 0x21;
116
117
118
119
120
121
122 const byte O4_ID_DMN_ACCEPT = 0x25;
123
124
125 const byte O4_ID_DMN_END = 0x84;
126
127
129
131
132 const byte O4_ID_PATH = 0x02;
133
134
135 const byte O4_ID_PATH_NAME = 0x1D;
136
137
138 const byte O4_ID_PATH_END = 0x85;
139
140
142
144
145 const byte O4_ID_COOKIE = 0x03;
146
147
148 const byte O4_ID_COOKIE_NAME = 0x10;
149
150
151 const byte O4_ID_COOKIE_VALUE = 0x11;
152
153
154 const byte O4_ID_COOKIE_EXPIRES = 0x12;
155
156
157 const byte O4_ID_COOKIE_USED = 0x13;
158
159
160 const byte O4_ID_COOKIE_DESC = 0x14;
161
162
163 const byte O4_ID_COOKIE_DESCURL = 0x15;
164
165
166 const byte O4_ID_COOKIE_RXDMN = 0x16;
167
168
169 const byte O4_ID_COOKIE_RXPATH = 0x17;
170
171
172 const byte O4_ID_COOKIE_PORT = 0x18;
173
174
175 const byte O4_ID_COOKIE_SECURE = 0x99;
176
177
178 const byte O4_ID_COOKIE_VER = 0x1A;
179
180
181 const byte O4_ID_COOKIE_SERVER = 0x9B;
182
183
184 const byte O4_ID_COOKIE_PROTECT = 0x9C;
185
186
187 const byte O4_ID_COOKIE_PREFIX = 0xA0;
188
189
190 const byte O4_ID_COOKIE_PWD = 0xA2;
191
192
193 const byte O4_ID_COOKIE_AUTH = 0xA3;
194
195
196 const byte O4_ID_COOKIE_3RD = 0xA4;
197
198
199 const byte O4_ID_COOKIE_END = 0xA9;
200
201 #endregion //Opera cookie format elements
202
203 #region Utilities
204
205
221 internal class Endian
222 {
223 private static readonly bool _LittleEndian;
224
225 static Endian()
231 {
232 _LittleEndian = BitConverter.IsLittleEndian;
233 }
234
235 public static short SwapInt16(short v)
243 {
244 return (short)(((v & 0xff) << 8) | ((v >> 8) & 0xff));
245 }
246
247 public static ushort SwapUInt16(ushort v)
255 {
256 return (ushort)(((v & 0xff) << 8) | ((v >> 8) & 0xff));
257 }
258
259 public static int SwapInt32(int v)
267 {
268 return (int)(((SwapInt16((short)v) & 0xffff) << 0x10) |
269 (SwapInt16((short)(v >> 0x10)) & 0xffff));
270 }
271
272 public static uint SwapUInt32(uint v)
280 {
281 return (uint)(((SwapUInt16((ushort)v) & 0xffff) << 0x10) |
282 (SwapUInt16((ushort)(v >> 0x10)) & 0xffff));
283 }
284
285 public static long SwapInt64(long v)
293 {
294 return (long)(((SwapInt32((int)v) & 0xffffffffL) << 0x20) |
295 (SwapInt32((int)(v >> 0x20)) & 0xffffffffL));
296 }
297
298 public static ulong SwapUInt64(ulong v)
306 {
307 return (ulong)(((SwapUInt32((uint)v) & 0xffffffffL) << 0x20) |
308 (SwapUInt32((uint)(v >> 0x20)) & 0xffffffffL));
309 }
310
311 public static bool IsBigEndian
315 {
316 get { return !_LittleEndian; }
317 }
318
319 public static bool IsLittleEndian
323 {
324 get { return _LittleEndian; }
325 }
326 }
327
328 #endregion //Utilities
329
330 #region Data Structures
331
332 private struct O4CookieHeader
340 {
341 public uint dwFileVer;
342 public uint dwAppVer;
343 public ushort wTagSize;
344 public ushort wRecSize;
345
346 public void Initialize()
352 {
353 this.dwFileVer = O4_VER_FILE;
354 this.dwAppVer = O4_VER_APP;
355 this.wTagSize = 1;
356 this.wRecSize = 2;
357 }
358
359 public void Load(byte[] bData, ref int idx)
368 {
369 this.dwFileVer = Endian.SwapUInt32(BitConverter.ToUInt32(bData, idx));
370 idx += 4;
371 this.dwAppVer = Endian.SwapUInt32(BitConverter.ToUInt32(bData, idx));
372 idx += 4;
373 this.wTagSize = Endian.SwapUInt16(BitConverter.ToUInt16(bData, idx));
374 idx += 2;
375 this.wRecSize = Endian.SwapUInt16(BitConverter.ToUInt16(bData, idx));
376 idx += 2;
377 }
378
379 public void Save(byte[] bData, ref int idx)
388 {
389 BitConverter.GetBytes(Endian.SwapUInt32(this.dwFileVer)).CopyTo(bData, idx);
390 idx += 4;
391 BitConverter.GetBytes(Endian.SwapUInt32(this.dwAppVer)).CopyTo(bData, idx);
392 idx += 4;
393 BitConverter.GetBytes(Endian.SwapUInt16(this.wTagSize)).CopyTo(bData, idx);
394 idx += 2;
395 BitConverter.GetBytes(Endian.SwapUInt16(this.wRecSize)).CopyTo(bData, idx);
396 idx += 2;
397 }
398 }
399
400 private struct O4Domain
408 {
409 public List<O4Domain> FDomains;
410 public string FName;
411 public byte FFilter;
412 public byte FMatch;
413 public byte FAccept;
414 public Dictionary<string, string> FPaths;
415 public List<O4Cookie> FCookies;
416
417 public void initialize(string Name)
425 {
426 FName = Name;
427 this.initialize();
428 }
429
430 public void initialize()
436 {
437 FDomains = new List<O4Domain>();
438 FPaths = new Dictionary<string, string>();
439 FCookies = new List<O4Cookie>();
440 }
441 }
442
443 #endregion //Data Structures
444
445 #region Parser
446
447 private void ParseCookieBytes(ref O4Domain domain, byte[] b, ref int idx)
457 {
458 ushort wTagLen;
459 byte cbTagID;
460
461
462 while (idx < b.Length)
463 {
464 cbTagID = b[idx++];
465
466
467 switch (cbTagID)
468 {
469 case O4_ID_DMN:
470
471
472 wTagLen = Endian.SwapUInt16(BitConverter.ToUInt16(b, idx));
473 idx += 2;
474
475
476
477
478
479
480 O4Domain d = new O4Domain();
481 d.initialize();
482 ParseCookieBytes(ref d, b, ref idx);
483 domain.FDomains.Add(d);
484 break;
485
486 case O4_ID_DMN_NAME:
487 domain.FName = this.ReadString(b, ref idx);
488 break;
489
490 case O4_ID_DMN_FILTER:
491 domain.FFilter = this.ReadByte(b, ref idx);
492 break;
493
494 case O4_ID_DMN_MATCH:
495 domain.FMatch = this.ReadByte(b, ref idx);
496 break;
497
498 case O4_ID_DMN_ACCEPT:
499 domain.FAccept = this.ReadByte(b, ref idx);
500 break;
501
502 case O4_ID_DMN_END:
503 return;
504
505 case O4_ID_PATH:
506 break;
507
508 case O4_ID_PATH_NAME:
509 domain.FPaths.Add(this.ReadString(b, ref idx), string.Empty);
510 break;
511
512 case O4_ID_PATH_END:
513 break;
514
515 case O4_ID_COOKIE:
516 AddCookie(ref domain, b, ref idx);
517 break;
518
519 default:
520 if (!(0 < (cbTagID & 0x80)))
521 {
522
523 idx += (2 + Endian.SwapUInt16(BitConverter.ToUInt16(b, idx)));
524 }
525
526 break;
527 }
528 }
529 }
530
531 private void AddCookie(ref O4Domain domain, byte[] b, ref int idx)
541 {
542 ushort wTagLen;
543 byte cbTagID;
544
545 O4Cookie cookie = new O4Cookie();
546
547
548 wTagLen = Endian.SwapUInt16(BitConverter.ToUInt16(b, idx));
549 idx += 2;
550
551
552
553
554
555 while (idx < b.Length)
556 {
557 cbTagID = b[idx++];
558
559
560 switch (cbTagID)
561 {
562 case O4_ID_COOKIE_NAME:
563 cookie.Name = this.ReadString(b, ref idx);
564 break;
565
566 case O4_ID_COOKIE_VALUE:
567 cookie.Value = this.ReadString(b, ref idx);
568 break;
569
570 case O4_ID_COOKIE_EXPIRES:
571 cookie.Expires = this.ReadLong(b, ref idx);
572 break;
573
574 case O4_ID_COOKIE_USED:
575 cookie.LastUsed = this.ReadLong(b, ref idx);
576 break;
577
578 case O4_ID_COOKIE_DESC:
579 cookie.Comment = this.ReadString(b, ref idx);
580 break;
581
582 case O4_ID_COOKIE_DESCURL:
583 cookie.CommentURL = this.ReadString(b, ref idx);
584 break;
585
586 case O4_ID_COOKIE_RXDMN:
587 cookie.RecvDomain = this.ReadString(b, ref idx);
588 break;
589
590 case O4_ID_COOKIE_RXPATH:
591 cookie.RecvPath = this.ReadString(b, ref idx);
592 break;
593
594 case O4_ID_COOKIE_PORT:
595 cookie.PortList = this.ReadString(b, ref idx);
596 break;
597
598 case O4_ID_COOKIE_SECURE:
599 cookie.Secure = true;
600 break;
601
602 case O4_ID_COOKIE_VER:
603 cookie.Version = this.ReadByte(b, ref idx);
604 break;
605
606 case O4_ID_COOKIE_SERVER:
607 cookie.Server = true;
608 break;
609
610 case O4_ID_COOKIE_PROTECT:
611 cookie.Protected = true;
612 break;
613
614 case O4_ID_COOKIE_PREFIX:
615 cookie.Prefixed = true;
616 break;
617
618 case O4_ID_COOKIE_PWD:
619 cookie.Password = true;
620 break;
621
622 case O4_ID_COOKIE_AUTH:
623 cookie.Authenticate = true;
624 break;
625
626 case O4_ID_COOKIE_3RD:
627 cookie.ThirdParty = true;
628 break;
629
630 case O4_ID_COOKIE_END:
631 goto EXIT_WHILE;
632
633 default:
634 if (!(0 < (cbTagID & 0x80)))
635 {
636
637 idx += (2 + Endian.SwapUInt16(BitConverter.ToUInt16(b, idx)));
638 }
639
640 break;
641 }
642 }
643 EXIT_WHILE:
644 domain.FCookies.Add(cookie);
645 }
646
647 private string ReadString(byte[] b, ref int idx)
656 {
657 ushort wStrLen = 0;
658
659
660 string lpszStr = string.Empty;
661 ASCIIEncoding encoding = new ASCIIEncoding();
662
663
664 wStrLen = Endian.SwapUInt16(BitConverter.ToUInt16(b, idx));
665 idx += 2;
666 if (0 < wStrLen)
667 {
668
669 lpszStr = encoding.GetString(b, idx, wStrLen);
670 idx += wStrLen;
671 }
672
673
674 return lpszStr;
675 }
676
677 private ulong ReadLong(byte[] b, ref int idx)
686 {
687 ushort wIntLen = 0;
688
689
690 ulong dwRtn = 0;
691
692
693 wIntLen = Endian.SwapUInt16(BitConverter.ToUInt16(b, idx));
694 idx += 2;
695 if (0 < wIntLen)
696 {
697
698 dwRtn = Endian.SwapUInt64(BitConverter.ToUInt64(b, idx));
699 idx += 8;
700 }
701 return dwRtn;
702 }
703
704 private byte ReadByte(byte[] b, ref int idx)
713 {
714 ushort wByteLen = 0;
715
716
717 byte bRtn = 0;
718
719
720 wByteLen = Endian.SwapUInt16(BitConverter.ToUInt16(b, idx));
721 idx += 2;
722 if (0 < wByteLen)
723 {
724
725 bRtn = b[idx++];
726 }
727 return bRtn;
728 }
729
730 #endregion //Parser
731
732 #region Class Constructor
733
734 public OpraCookieJar() { }
740
741 public OpraCookieJar(byte[] Bytes)
749 {
750 _RawBytes = Bytes;
751 }
752
753 public OpraCookieJar(string FilePath)
761 {
762 string strTemp;
763 FileStream f;
764 FileInfo fi;
765 ASCIIEncoding encoder;
766 try
767 {
768 byte[] bytes;
769
770 encoder = new ASCIIEncoding();
771 strTemp = FilePath + ".temp";
772
773
774
775 File.Copy(FilePath, strTemp, true);
776
777
778 fi = new FileInfo(strTemp);
779 bytes = new byte[fi.Length];
780 f = fi.OpenRead();
781 f.Read(bytes, 0, bytes.Length);
782 f.Close();
783
784
785 File.Delete(strTemp);
786
787
788 RawBytes = bytes;
789 }
790 catch (Exception)
791 {
792 throw;
793 }
794 }
795
796 #endregion //Class Constructor
797
798 #region Properties
799
800 public byte[] RawBytes
804 {
805 get { return _RawBytes; }
806 set
807 {
808 _RawBytes = value;
809
810 int idx = 0;
811 CookieFileHeader.Load(_RawBytes, ref idx);
812 _Root = new O4Domain();
813 _Root.initialize("Root");
814 ParseCookieBytes(ref _Root, _RawBytes, ref idx);
815 }
816 }
817
818 #endregion //Properties
819
820 private List<O4Cookie> GetCookieRecurse(O4Domain Domain, string[] Names, int iName)
830 {
831 List<O4Cookie> ret = null;
832
833 if (iName < Names.Length)
834 {
835 foreach (O4Domain d in Domain.FDomains)
836 {
837 if (d.FName.ToUpper().Equals(Names[iName].ToUpper()))
838 {
839 ret = GetCookieRecurse(d, Names, iName + 1);
840 }
841 else
842 {
843 ret = GetCookieRecurse(d, Names, iName);
844 }
845
846 if (ret != null)
847 return ret;
848 }
849 }
850 else
851 {
852 if (0 < Domain.FCookies.Count)
853 {
854 return Domain.FCookies;
855 }
856 }
857 return null;
858 }
859
860 public List<O4Cookie> GetCookies(string HostName)
869 {
870 List<O4Cookie> cookies = null;
871 string[] aryNames = HostName.Split('.');
872 Array.Reverse(aryNames);
873
874
875 cookies = GetCookieRecurse(_Root, aryNames, 0);
876 return cookies;
877 }
878 }
879 }