1 /*
2  * TLVs (Type, Length, Value)
3  *
4  * Copyright (C) 2001 Barnaby Gray <barnaby@beedesign.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
19  *
20  */
21 
22 #ifndef TLV_H
23 #define TLV_H
24 
25 #include <string>
26 #include <map>
27 
28 #include <string.h>
29 #include <stdlib.h>
30 
31 #include "Xml.h"
32 #include "exceptions.h"
33 #include "buffer.h"
34 #include "constants.h"
35 #include "ICQ.h"
36 #include "Capabilities.h"
37 
38 namespace ICQ2000 {
39 
40   // ------------- TLV numerical constants ------------
41 
42   /*
43    * TLV types
44    * Originally I thought TLV types were distinct within
45    * each channel, but in Messages it turns out they are only
46    * distinct within each block. To complicate matters you
47    * then get TLVs inside TLVs..
48    * Solution: the TLV parser must be told what it is expecting
49    * to parse so that the correct TLV types are associated
50    */
51 
52   enum TLV_ParseMode { TLV_ParseMode_Channel01,
53 		       TLV_ParseMode_Channel02,
54 		       TLV_ParseMode_Channel04,
55 		       TLV_ParseMode_MessageBlock,
56 		       TLV_ParseMode_AdvMsgBlock,
57 		       TLV_ParseMode_InMessageData,
58 		       TLV_ParseMode_InAdvMsgData,
59 		       TLV_ParseMode_SBL,
60 		       TLV_ParseMode_SBL_Rights
61   };
62 
63   // Channel 0x0001
64   const unsigned short TLV_Screenname = 0x0001;
65   const unsigned short TLV_Password = 0x0002;
66   const unsigned short TLV_ClientProfile = 0x0003;
67   const unsigned short TLV_UserInfo = 0x0005;
68   const unsigned short TLV_Cookie = 0x0006;
69   const unsigned short TLV_CountryCode = 0x000e;
70   const unsigned short TLV_Language = 0x000f;
71   const unsigned short TLV_ClientBuildMinor = 0x0014;
72   const unsigned short TLV_ClientType = 0x0016;
73   const unsigned short TLV_ClientVersionMajor = 0x0017;
74   const unsigned short TLV_ClientVersionMinor = 0x0018;
75   const unsigned short TLV_ClientICQNumber = 0x0019;
76   const unsigned short TLV_ClientBuildMajor = 0x001a;
77   const unsigned short TLV_Type94 = 0x0094;
78 
79   // Channel 0x0002
80   const unsigned short TLV_UserClass = 0x0001;
81   const unsigned short TLV_SignupDate = 0x0002;
82   const unsigned short TLV_SignonDate = 0x0003;
83   const unsigned short TLV_Port = 0x0004; // ??
84   const unsigned short TLV_UserInfoCapabilities = 0x0005;
85   const unsigned short TLV_Status = 0x0006;
86   const unsigned short TLV_Unknown = 0x0008; // ??
87   const unsigned short TLV_IPAddress = 0x000a;
88   const unsigned short TLV_WebAddress = 0x000b;
89   const unsigned short TLV_LANDetails = 0x000c;
90   const unsigned short TLV_Capabilities = 0x000d;
91   const unsigned short TLV_TimeOnline = 0x000f;
92   const unsigned short TLV_ContactNickname = 0x0131;
93   const unsigned short TLV_AuthAwaited = 0x0066;
94 
95   // Channel 0x0004
96   // const unsigned short TLV_Screenname = 0x0001;
97   const unsigned short TLV_ErrorURL = 0x0004;
98   const unsigned short TLV_Redirect = 0x0005;
99   // const unsigned short TLV_Cookie = 0x0006;
100   const unsigned short TLV_ErrorCode = 0x0008;
101   const unsigned short TLV_DisconnectReason = 0x0009;
102   const unsigned short TLV_DisconnectMessage = 0x000b;
103   const unsigned short TLV_Unknown3 = 0x000c;
104   const unsigned short TLV_EmailAddress = 0x0011;
105   const unsigned short TLV_RegStatus = 0x0013;
106 
107   // Message Block
108   const unsigned short TLV_MessageData = 0x0002;
109   const unsigned short TLV_ServerAckRequested = 0x0003;
110   const unsigned short TLV_MessageIsAutoResponse = 0x0004;
111   const unsigned short TLV_ICQData = 0x0005;
112 
113   // Advanced Message Block
114   const unsigned short TLV_AdvMsgData = 0x0005;
115 
116   // In Message Data
117   const unsigned short TLV_Unknown0501 = 0x0501;
118   const unsigned short TLV_MessageText = 0x0101;
119 
120   // In Advanced Message Data
121   const unsigned short TLV_AdvMsgBody = 0x2711;
122   // loads more - but we don't parse them yet
123 
124   // In Server-based list data
125   const unsigned short TLV_SBL_Await_Auth = 0x0066;
126   const unsigned short TLV_SBL_Ids        = 0x00c8;
127   const unsigned short TLV_SBL_Visibility = 0x00ca;
128   const unsigned short TLV_SBL_ICQTIC     = 0x00cd;
129   const unsigned short TLV_SBL_ImportTime = 0x00d4;
130   const unsigned short TLV_SBL_Nick       = 0x0131;
131   const unsigned short TLV_SBL_SMS_No     = 0x013a;
132 
133   // ------------- abstract TLV classes ---------------
134 
135   class TLV {
136    public:
~TLV()137     virtual ~TLV() { }
138 
139     virtual unsigned short Type() const = 0;
140     virtual unsigned short Length() const = 0;
141   };
142 
143   // -- Inbound TLV --
144   class InTLV : public TLV {
145    public:
146     virtual void ParseValue(Buffer& b) = 0;
147 
148     static InTLV* ParseTLV(Buffer& b, TLV_ParseMode pm);
149   };
150 
151   // -- Outbound TLV --
152   class OutTLV : public TLV {
153    protected:
154     virtual void OutputHeader(Buffer& b) const;
155     virtual void OutputValue(Buffer& b) const = 0;
156 
157    public:
158     virtual void Output(Buffer& b) const;
159   };
160 
161   // -------------- base classes ----------------------
162 
163   class CharTLV : public OutTLV, public InTLV {
164    protected:
165     unsigned char m_value;
166 
167     virtual void OutputValue(Buffer& b) const;
168 
169    public:
170     CharTLV();
171     CharTLV(unsigned char n);
172 
Length()173     unsigned short Length() const { return 1; }
174 
175     virtual void ParseValue(Buffer& b);
Value()176     virtual unsigned char Value() const { return m_value; }
177   };
178 
179   class ShortTLV : public OutTLV, public InTLV {
180    protected:
181     unsigned short m_value;
182 
183     virtual void OutputValue(Buffer& b) const;
184 
185    public:
186     ShortTLV();
187     ShortTLV(unsigned short n);
188 
Length()189     unsigned short Length() const { return 2; }
190 
191     virtual void ParseValue(Buffer& b);
Value()192     virtual unsigned short Value() const { return m_value; }
193   };
194 
195 
196   class LongTLV : public OutTLV, public InTLV {
197    protected:
198     unsigned int m_value;
199 
200     virtual void OutputValue(Buffer& b) const;
201 
202    public:
203     LongTLV();
204     LongTLV(unsigned int n);
205 
Length()206     unsigned short Length() const { return 4; }
207 
208     virtual void ParseValue(Buffer& b);
Value()209     virtual unsigned int Value() const { return m_value; }
210   };
211 
212 
213   class StringTLV : public OutTLV, public InTLV {
214    protected:
215     std::string m_value;
216 
217     virtual void OutputValue(Buffer& b) const;
218 
219    public:
220     StringTLV();
221     StringTLV(const std::string& val);
222 
Length()223     unsigned short Length() const { return m_value.size(); }
224 
225     virtual void ParseValue(Buffer& b);
Value()226     virtual std::string Value() const { return m_value; }
227   };
228 
229 
230   // --------------- actual classes -------------------
231 
232   class ErrorURLTLV : public StringTLV {
233    public:
ErrorURLTLV()234     ErrorURLTLV() { }
Type()235     unsigned short Type() const { return TLV_ErrorURL; }
236   };
237 
238   class ErrorCodeTLV : public ShortTLV {
239    public:
ErrorCodeTLV()240     ErrorCodeTLV() { }
Type()241     unsigned short Type() const { return TLV_ErrorCode; }
242   };
243 
244   class DisconnectReasonTLV : public ShortTLV {
245    public:
DisconnectReasonTLV()246     DisconnectReasonTLV() { }
Type()247     unsigned short Type() const { return TLV_DisconnectReason; }
248   };
249 
250   class DisconnectMessageTLV : public StringTLV {
251    public:
DisconnectMessageTLV()252     DisconnectMessageTLV() { }
Type()253     unsigned short Type() const { return TLV_DisconnectMessage; }
254   };
255 
256   class ScreenNameTLV : public StringTLV {
257    public:
258     ScreenNameTLV();
259     ScreenNameTLV(const std::string& val);
260 
Type()261     unsigned short Type() const { return TLV_Screenname; }
262   };
263 
264   class PasswordTLV : public OutTLV {
265    protected:
266     std::string m_password;
267 
268     void OutputValue(Buffer& b) const;
269 
270    public:
271     PasswordTLV(const std::string& pw);
272 
Type()273     unsigned short Type() const { return TLV_Password; }
Length()274     unsigned short Length() const { return m_password.size(); }
275   };
276 
277   const unsigned char ALLOWDIRECT_EVERYONE = 0x00;
278   const unsigned char ALLOWDIRECT_AUTHORIZATION = 0x10;
279   const unsigned char ALLOWDIRECT_CONTACTLIST = 0x20;
280 
281   const unsigned char WEBAWARE_NORMAL = 0x02;
282   const unsigned char WEBAWARE_WEBAWARE = 0x03;
283 
284   class StatusTLV : public OutTLV, public InTLV {
285    private:
286     unsigned char m_allowDirect;
287     unsigned char m_webAware;
288     unsigned short m_status;
289 
290    protected:
291     void OutputValue(Buffer& b) const;
292     void ParseValue(Buffer& b);
293 
294    public:
StatusTLV(unsigned char ad,unsigned char wa,unsigned short st)295     StatusTLV(unsigned char ad, unsigned char wa, unsigned short st)
296       : m_allowDirect(ad), m_webAware(wa), m_status(st)
297       { }
StatusTLV()298     StatusTLV() { }
299 
Type()300     unsigned short Type() const { return TLV_Status; }
Length()301     unsigned short Length() const { return 4; }
302 
getAllowDirect()303     unsigned char getAllowDirect() { return m_allowDirect; }
getWebAware()304     unsigned char getWebAware() { return m_webAware; }
getStatus()305     unsigned short getStatus() { return m_status; }
getBirthday()306     bool getBirthday() { return ((m_webAware & 0x08) == 0x08); }
307 
setAllowDirect(unsigned char m)308     void setAllowDirect(unsigned char m) { m_allowDirect = m; }
setWebAware(unsigned char m)309     void setWebAware(unsigned char m) { m_webAware = m; }
setStatus(unsigned short m)310     void setStatus(unsigned short m) { m_status = m; }
311   };
312 
313   // -- Client*TLVs --
314 
315   class ClientProfileTLV : public StringTLV {
316    public:
ClientProfileTLV(const std::string & val)317     ClientProfileTLV(const std::string& val) : StringTLV(val) { }
Type()318     unsigned short Type() const { return TLV_ClientProfile; }
319   };
320 
321   class ClientTypeTLV : public ShortTLV {
322    public:
ClientTypeTLV(unsigned short n)323     ClientTypeTLV(unsigned short n) : ShortTLV(n) { }
Type()324     unsigned short Type() const { return TLV_ClientType; }
325   };
326 
327   class ClientVersionMajorTLV : public ShortTLV {
328    public:
ClientVersionMajorTLV(unsigned short n)329     ClientVersionMajorTLV(unsigned short n) : ShortTLV(n) { }
Type()330     unsigned short Type() const { return TLV_ClientVersionMajor; }
331   };
332 
333   class ClientVersionMinorTLV : public ShortTLV {
334    public:
ClientVersionMinorTLV(unsigned short n)335     ClientVersionMinorTLV(unsigned short n) : ShortTLV(n) { }
Type()336     unsigned short Type() const { return TLV_ClientVersionMinor; }
337   };
338 
339   class ClientICQNumberTLV : public ShortTLV {
340    public:
ClientICQNumberTLV(unsigned short n)341     ClientICQNumberTLV(unsigned short n) : ShortTLV(n) { }
Type()342     unsigned short Type() const { return TLV_ClientICQNumber; }
343   };
344 
345   class ClientBuildMajorTLV : public ShortTLV {
346    public:
ClientBuildMajorTLV(unsigned short n)347     ClientBuildMajorTLV(unsigned short n) : ShortTLV(n) { }
Type()348     unsigned short Type() const { return TLV_ClientBuildMajor; }
349   };
350 
351   class ClientBuildMinorTLV : public LongTLV {
352    public:
ClientBuildMinorTLV(unsigned int n)353     ClientBuildMinorTLV(unsigned int n) : LongTLV(n) { }
Type()354     unsigned short Type() const { return TLV_ClientBuildMinor; }
355   };
356 
357   class CountryCodeTLV : public StringTLV {
358    public:
CountryCodeTLV(std::string val)359     CountryCodeTLV(std::string val) : StringTLV(val) { }
Type()360     unsigned short Type() const { return TLV_CountryCode; }
361   };
362 
363   class LanguageTLV : public StringTLV {
364    public:
LanguageTLV(const std::string & val)365     LanguageTLV(const std::string& val) : StringTLV(val) { }
Type()366     unsigned short Type() const { return TLV_Language; }
367   };
368 
369   class Type94TLV : public CharTLV
370   {
371    public:
Type94TLV(unsigned char n)372     Type94TLV(unsigned char n) : CharTLV(n) { }
Type()373     unsigned short Type() const { return TLV_Type94; }
374   };
375 
376   // --
377 
378   class WebAddressTLV : public StringTLV {
379    public:
WebAddressTLV()380     WebAddressTLV() { }
Type()381     unsigned short Type() const { return TLV_WebAddress; }
382   };
383 
384   class UserClassTLV : public ShortTLV {
385    public:
UserClassTLV()386     UserClassTLV() { }
Type()387     unsigned short Type() const { return TLV_UserClass; }
388   };
389 
390   class TimeOnlineTLV : public LongTLV {
391    public:
TimeOnlineTLV()392     TimeOnlineTLV() { }
Type()393     unsigned short Type() const { return TLV_TimeOnline; }
394   };
395 
396   class SignupDateTLV : public LongTLV {
397    public:
SignupDateTLV()398     SignupDateTLV() { }
Type()399     unsigned short Type() const { return TLV_SignupDate; }
400   };
401 
402   class SignonDateTLV : public LongTLV {
403    public:
SignonDateTLV()404     SignonDateTLV() { }
Type()405     unsigned short Type() const { return TLV_SignonDate; }
406   };
407 
408   class UnknownTLV : public ShortTLV {
409    public:
UnknownTLV()410     UnknownTLV() : ShortTLV(0) { }
Type()411     unsigned short Type() const { return TLV_Unknown; }
412   };
413 
414   class IPAddressTLV : public LongTLV {
415    public:
IPAddressTLV()416     IPAddressTLV() { }
Type()417     unsigned short Type() const { return TLV_IPAddress; }
418   };
419 
420   class PortTLV : public ShortTLV {
421    public:
PortTLV()422     PortTLV() { }
Type()423     unsigned short Type() const { return TLV_Port; }
424   };
425 
426   class UserInfoCapabilitiesTLV : public OutTLV {
427    private:
428     Capabilities m_capabilities;
429 
430    public:
431     UserInfoCapabilitiesTLV();
Type()432     unsigned short Type() const { return TLV_UserInfoCapabilities; }
433     unsigned short Length() const;
434 
435     void OutputValue(Buffer& b) const;
436   };
437 
438   class CapabilitiesTLV : public InTLV {
439    private:
440     Capabilities m_capabilities;
441 
442    public:
CapabilitiesTLV()443     CapabilitiesTLV() { }
Type()444     unsigned short Type() const { return TLV_Capabilities; }
Length()445     unsigned short Length() const { return m_capabilities.get_length(); }
446 
447     Capabilities get_capabilities() const;
448 
449     void ParseValue(Buffer& b);
450   };
451 
452   class RedirectTLV : public InTLV {
453    private:
454     std::string m_server;
455     unsigned short m_port;
456 
457    public:
RedirectTLV()458     RedirectTLV() { }
459 
Length()460     unsigned short Length() const { return m_server.size(); }
Type()461     unsigned short Type() const { return TLV_Redirect; }
462 
463     void ParseValue(Buffer& b);
464 
getHost()465     std::string getHost() { return m_server; }
getPort()466     unsigned short getPort() { return m_port; }
467   };
468 
469   class CookieTLV : public InTLV, public OutTLV {
470    private:
471     unsigned char *m_value;
472     unsigned short m_length;
473 
474    public:
CookieTLV()475     CookieTLV() : m_value(NULL), m_length(0) { }
476     CookieTLV(const unsigned char *ck, unsigned short len);
477     ~CookieTLV();
478 
Length()479     unsigned short Length() const { return m_length; }
Type()480     unsigned short Type() const { return TLV_Cookie; }
481 
482     void ParseValue(Buffer& b);
483     void OutputValue(Buffer& b) const;
484 
Value()485     const unsigned char* Value() { return m_value; }
486   };
487 
488   // can go out as well
489   class LANDetailsTLV : public InTLV, public OutTLV {
490    private:
491     unsigned int m_lan_ip;
492     unsigned short m_lan_port, m_firewall;
493     unsigned char m_tcp_version;
494     unsigned int m_dc_cookie;
495 
496    public:
497     LANDetailsTLV();
498     LANDetailsTLV(unsigned int ip, unsigned short port);
499 
Length()500     unsigned short Length() const { return 0; } // varies
Type()501     unsigned short Type() const { return TLV_LANDetails; }
502 
getLanIP()503     unsigned int getLanIP() const { return m_lan_ip; }
getLanPort()504     unsigned short getLanPort() const { return m_lan_port; }
getFirewall()505     unsigned short getFirewall() const { return m_firewall; }
getTCPVersion()506     unsigned char getTCPVersion() const { return m_tcp_version; }
getDCCookie()507     unsigned int getDCCookie() const { return m_dc_cookie; }
508 
509     void ParseValue(Buffer& b);
510     void OutputValue(Buffer& b) const;
511   };
512 
513   class RawTLV : public InTLV {
514    protected:
515     unsigned short m_type;
516     unsigned short m_length;
517 
518    public:
519     RawTLV(unsigned short type);
520 
Type()521     unsigned short Type() const { return m_type; }
Length()522     unsigned short Length() const { return m_length; }
523     void ParseValue(Buffer& b);
524   };
525 
526   const unsigned short MESSAGETEXT_FLAG1_UCS2      = 0x02;
527   const unsigned short MESSAGETEXT_FLAG1_ISO8859_1 = 0x03;
528 
529   class MessageTextTLV : public InTLV {
530    protected:
531     std::string m_message;
532     unsigned short m_flag1, m_flag2;
533 
534    public:
MessageTextTLV()535     MessageTextTLV()
536       : m_message(), m_flag1(0), m_flag2(0) { }
537 
getMessage()538     std::string getMessage() { return m_message; }
getFlag1()539     unsigned short getFlag1() { return m_flag1; }
getFlag2()540     unsigned short getFlag2() { return m_flag1; }
541 
542     void ParseValue(Buffer& b);
Type()543     unsigned short Type() const { return TLV_MessageText; }
Length()544     unsigned short Length() const { return 0; }
545   };
546 
547   class MessageDataTLV : public InTLV {
548     MessageTextTLV mttlv;
549 
550    public:
551     MessageDataTLV();
552 
getMessage()553     std::string getMessage() { return mttlv.getMessage(); }
getFlag1()554     unsigned short getFlag1() { return mttlv.getFlag1(); }
getFlag2()555     unsigned short getFlag2() { return mttlv.getFlag2(); }
556 
557     void ParseValue(Buffer& b);
Type()558     unsigned short Type() const { return TLV_MessageData; }
Length()559     unsigned short Length() const { return 0; }
560   };
561 
562   class AdvMsgBodyTLV : public InTLV {
563    protected:
564     ICQSubType *m_icqsubtype;
565 
566    public:
567     AdvMsgBodyTLV();
568     ~AdvMsgBodyTLV();
569 
570     ICQSubType* grabICQSubType();
571 
572     void ParseValue(Buffer& b);
Type()573     unsigned short Type() const { return TLV_AdvMsgBody; }
Length()574     unsigned short Length() const { return 0; }
575   };
576 
577   class AdvMsgDataTLV : public InTLV {
578     ICQSubType *m_icqsubtype;
579 
580    public:
581     AdvMsgDataTLV();
582     ~AdvMsgDataTLV();
583 
584     ICQSubType* grabICQSubType();
585 
586     void ParseValue(Buffer& b);
Type()587     unsigned short Type() const { return TLV_AdvMsgData; }
Length()588     unsigned short Length() const { return 0; }
589   };
590 
591   // ============================================================================
592   //  SBL TLVs
593   // ============================================================================
594 
595   class SBLAwaitAuthTLV : public InTLV
596   {
597    public:
SBLAwaitAuthTLV()598     SBLAwaitAuthTLV() { }
599 
Type()600     unsigned short Type() const { return TLV_SBL_Await_Auth; }
Length()601     unsigned short Length() const { return 0; }
602     void ParseValue(Buffer& b);
603   };
604 
605   class SBLIdsTLV : public InTLV
606   {
607    public:
SBLIdsTLV()608     SBLIdsTLV() { }
609 
Type()610     unsigned short Type() const { return TLV_SBL_Ids; }
Length()611     unsigned short Length() const { return 0; }
612     void ParseValue(Buffer& b);
613   };
614 
615   class SBLVisibilityTLV : public CharTLV
616   {
617    public:
SBLVisibilityTLV()618     SBLVisibilityTLV() { }
Type()619     unsigned short Type() const { return TLV_SBL_Visibility; }
620   };
621 
622   class SBLICQTICTLV : public StringTLV
623   {
624    public:
SBLICQTICTLV()625     SBLICQTICTLV() { }
Type()626     unsigned short Type() const { return TLV_SBL_ICQTIC; }
627   };
628 
629   class SBLImportTimeTLV : public LongTLV
630   {
631    public:
SBLImportTimeTLV()632     SBLImportTimeTLV() { }
Type()633     unsigned short Type() const { return TLV_SBL_ImportTime; }
634   };
635 
636   class SBLNickTLV : public StringTLV
637   {
638    public:
SBLNickTLV()639     SBLNickTLV() { }
Type()640     unsigned short Type() const { return TLV_SBL_Nick; }
641   };
642 
643   class SBLSMSNoTLV : public StringTLV
644   {
645    public:
SBLSMSNoTLV()646     SBLSMSNoTLV() { }
Type()647     unsigned short Type() const { return TLV_SBL_SMS_No; }
648   };
649 
650   // --------------- ICQDataTLV ------------------
651 
652   class ICQDataTLV : public InTLV {
653    private:
654     ICQSubType *m_icqsubtype;
655 
656    public:
657     ICQDataTLV();
658     ~ICQDataTLV();
659 
660     ICQSubType* getICQSubType() const;
661     ICQSubType* grabICQSubType();
662 
663     void ParseValue(Buffer& b);
Type()664     unsigned short Type() const { return TLV_ICQData; }
Length()665     unsigned short Length() const { return 0; }
666 
667   };
668 
669   // ---------------- TLV List -------------------
670 
671   class TLVList {
672    private:
673     std::map<unsigned short,InTLV*> tlvmap;
674    public:
675     TLVList();
676     ~TLVList();
677 
678     void Parse(Buffer& b, TLV_ParseMode pm, unsigned short no_tlvs);
679     void ParseByLength(Buffer& b, TLV_ParseMode pm, unsigned int len);
680 
681     bool exists(unsigned short type);
682     InTLV* & operator[](unsigned short type);
683 
684   };
685 
686   Buffer& operator<<(Buffer& b, const ICQ2000::OutTLV& t);
687 
688 }
689 
690 #endif
691