001    package com.skype.api;
002    
003    import com.skype.ipc.SidRoot;
004    import com.skype.ipc.SidObject;
005    import com.skype.ipc.EnumConverting;
006    import com.skype.ipc.PropertyEnumConverting;
007    import com.skype.ipc.Decoding;
008    import com.skype.ipc.Encoding;
009    import com.skype.ipc.Encoding;
010    import java.io.IOException;
011    import com.skype.ipc.PropertyEnumConverting;
012    import com.skype.api.Skype;
013    import com.skype.ipc.SidGetResponding;
014    
015    /**
016     * Events in a conversation context are expressed as Messages. It is therefore useful to think of Message objects as events, rather than specifically text chat messages. 
017     * 
018     * Message member functions all return a Boolean indicating the success (true) or failure (false) of processing the request itself (transport, runtime availability, and so forth) - not the success or failure of its associated functionality. For example, Message.Edit returns true if it was able to make a determination, and its result parameter reflects whether this Message can be edited. Similarly, Message.Edit returns false if it was unable to make a determination, and the value of its result parameter is undefined. 
019     * 
020     * Message member functions that are specific to a Message TYPE return false if this Message is not of that type. For example, Message.GetVoiceMessage will return false if this Message's type is not POSTED_VOICE_MESSAGE. 
021     * 
022     * The actual meaning of a Message can be determined by its P_TYPE property. The meanings of most other Message properties depend on the value of P_TYPE. For example, let's take P_BODY_XML property. 
023     * 
024     * Following messages have a text entered by the user as a body. It may contain emoticons, URLs, etc. 
025     *  - POSTED_TEXT 
026     *  - POSTED_EMOTE 
027     *  - SET_METADATA 
028     *  - REQUESTED_AUTH 
029     * 
030     * Following messages have a custom XML format for the body (see the specific section on these message types for details): 
031     *  - POSTED_CONTACTS 
032     *  - POSTED_VOICE_MESSAGE 
033     *  - POSTED_FILES 
034     *  - POSTED_SMS 
035     *  - STARTED_LIVESESSION and ENDED_LIVESESSION (same format) 
036     * 
037     * Following messages do not use the body property: 
038     *  - SPAWNED_CONFERENCE 
039     *  - ADDED_CONSUMERS 
040     *  - ADDED_APPLICANTS 
041     *  - RETIRED_OTHERS 
042     *  - RETIRED 
043     *  - SET_RANK 
044     *  - HAS_BIRTHDAY 
045     *  - GRANTED_AUTH 
046     *  - BLOCKED 
047     * 
048     * Messages such as POSTED_TEXT use a small subset of a HTML-like markup to control the visual representation of the text. This markup is used by POSTED_TEXT and POSTED_EMOTE, but also for the conversation topic (CONVERSATION_META_TOPIC property and the body of the SET_METADATA message) and for authorization requests. 
049     * 
050     * Having chat messages in XML format means that all formatting is indicated by XML tags. This includes emoticons and URls. The big advantage is that it makes the parsing of the message by the UI much easier. The UI does not need to do emoticons or URL detection, this is already done and it only needs to look for the XML tags. 
051     * 
052     * For text messages, it is possible for the UI to simply ignore (meaning strip) the XML and the message will be understandable fine, it will only have lost some formatting. 
053     * 
054     * But it is obviously nicer to display at least the most commonly used tags. 
055     * 
056     * To strip the XML: 
057     *  - if they have the alt="sometext" attribute set, return sometext as the output of that tag and ignore the rest of tag and all nested sub tags 
058     *  - if no alt="" attribute set, use tag content as output - <sometag>hereissomething</sometag> is output as hereissomething
059     *  - if no alt="" and no tag content, ignore the tag altogether (return nothing) 
060     * Skype for Windows supports displaying many XML tags, but only a sub-set is regularly used and should be supported by the UI for a good experience. These are the ones described here. 
061     * Animated emoticons 
062     * Emoticons are encoded with the "ss" tag. The element content is the plain text representation. It has a "type" attribute indicating the emoticons canonical name. Example:  
063     * @code 
064     * Hi <ss type="smile">:-)</ss>  
065     * </CODE> 
066     * 
067     * Flag emoticons 
068     * Flag emoticons are little flags. They are encoded with the "flag" tag. The element contents is the plain text representation and it has a "country" attribute which is a 2-letter ISO-3166 country code. The user can enter a flag using "(flag:XX)", where XX is a valid ISO country code. Example:  
069     * @code 
070     * I am in <flag country="cc">CC</flag>  
071     * </CODE> 
072     * 
073     * Links 
074     * If the library detects a URL, it will encode it using the html "a" tag, with the "href" attribute indicating the URL. The plain text representation is what the user originally typed. Example:  
075     * @code 
076     * I am in <a href="http://wwww.skype.com">www.skype.com</a> 
077     * </CODE> 
078     * 
079     * Alert matches 
080     * When a conversation is configured to display only alerts if specific words are present in the message (see "/alertson [text to match]" command), if a message matches the alert, it will be marked with the <alertmatch> tag. This allows the UI to highlight the word matching. Example:  
081     * @code 
082     * Maybe <alertmatch>Vincent</alertmatch> knows the answer  
083     * </CODE> 
084     * 
085     * Bold, italic, etc 
086     * Skype for Windows also supports displaying bold and italic text, using the "b" and "i" tags. 
087     * 
088     * Encoding messages 
089     * When sending a chat message via PostText(), there is the possibility to indicate if the library should do the XML encoding, or if the message has already been encoded. Usually, the UI can let library do the encoding. This is the case when the message does not contain any specific formatting. It may contain emoticons or URls, which will be detected by the library encoder and converted into XML tags. 
090     * If the message has some more complex encoding, such as a quote or some bold text, it is up to the UI to encode the message. 
091     */
092    public final class Message extends SidObject {
093            /** The P_TYPE property determines the actual meaning of the Message object. Only Messages of POSTED_TEXT type contain actual text messages. The meaning and content of the rest of the message properties are largely dependant of the value of the Message.P_TYPE.  */
094            public enum Type implements EnumConverting {
095                    /** Conference metadata were changed */
096                    SET_METADATA        (2),
097                    /** A conference was spawned from this dialog */
098                    SPAWNED_CONFERENCE  (4),
099                    /** Some users were added to the conference */
100                    ADDED_CONSUMERS     (10),
101                    /** Some users are applying to be added to the conference */
102                    ADDED_APPLICANTS    (11),
103                    /** User was kicked from the conference */
104                    RETIRED_OTHERS      (12),
105                    /** User left the conference */
106                    RETIRED             (13),
107                    /** Changed the rank of a user in the Conversation (multichat administration)  */
108                    SET_RANK            (21),
109                    /** A live session started */
110                    STARTED_LIVE_SESSION(30),
111                    /** A live session ended */
112                    ENDED_LIVE_SESSION  (39),
113                    /** User requested authorization */
114                    REQUESTED_AUTH      (50),
115                    /** User was granted authorization. Notification message that user is now an authorized contact (of the local user).  */
116                    GRANTED_AUTH        (51),
117                    /** User was blocked */
118                    BLOCKED             (53),
119                    /** A text message */
120                    POSTED_TEXT         (61),
121                    /** An emote ('John Doe is laughing', cf /me chat command) */
122                    POSTED_EMOTE        (60),
123                    /** The message represents (a set of) contact card(s) posted in the conversation. One message can contain more than one contact cards. The contacts can be retrieved from the message by parsing them out from the P_BODY_XML property. For more information, see Conversation.PostContacts  */
124                    POSTED_CONTACTS     (63),
125                    /** The message represents an SMS object that was posted in the Conversation. See Conversation.PostSMS for more details. The Sms object itself can be retrieved from the Message with Message.GetSms The message BODY_XML contains a set of SMS properties, such as status, failurereason, targets, price and timestamp.  */
126                    POSTED_SMS          (64),
127                    /** Deprecated, never sent */
128                    POSTED_ALERT        (65),
129                    /** A voicemail */
130                    POSTED_VOICE_MESSAGE(67),
131                    /** The message represents a (list of) file transfers that were posted in the Conversation with Conversation.PostFiles. Transfer objects can be retrieved from the Message with Message.GetTransfers  */
132                    POSTED_FILES        (68),
133                    /** Currently unused.  */
134                    POSTED_INVOICE      (69),
135                    /** The message represents a Contact birthday notification.  */
136                    HAS_BIRTHDAY        (110);
137                    private final int key;
138                    Type(int key) {
139                            this.key = key;
140                    };
141                    public int getId()   { return key; }
142                    public EnumConverting getDefault() { return SET_METADATA; }
143                    public EnumConverting convert(int from) { return Type.get(from); }
144                    public EnumConverting[] getArray(final int size) { return new Type[size]; }             public static Type get(int from) {
145                            switch (from) {
146                            case   2: return SET_METADATA;
147                            case   4: return SPAWNED_CONFERENCE;
148                            case  10: return ADDED_CONSUMERS;
149                            case  11: return ADDED_APPLICANTS;
150                            case  12: return RETIRED_OTHERS;
151                            case  13: return RETIRED;
152                            case  21: return SET_RANK;
153                            case  30: return STARTED_LIVE_SESSION;
154                            case  39: return ENDED_LIVE_SESSION;
155                            case  50: return REQUESTED_AUTH;
156                            case  51: return GRANTED_AUTH;
157                            case  53: return BLOCKED;
158                            case  61: return POSTED_TEXT;
159                            case  60: return POSTED_EMOTE;
160                            case  63: return POSTED_CONTACTS;
161                            case  64: return POSTED_SMS;
162                            case  65: return POSTED_ALERT;
163                            case  67: return POSTED_VOICE_MESSAGE;
164                            case  68: return POSTED_FILES;
165                            case  69: return POSTED_INVOICE;
166                            case 110: return HAS_BIRTHDAY;
167                            }
168                            return SET_METADATA;
169                    }
170                    public static final int SET_METADATA_VALUE         =   2;
171                    public static final int SPAWNED_CONFERENCE_VALUE   =   4;
172                    public static final int ADDED_CONSUMERS_VALUE      =  10;
173                    public static final int ADDED_APPLICANTS_VALUE     =  11;
174                    public static final int RETIRED_OTHERS_VALUE       =  12;
175                    public static final int RETIRED_VALUE              =  13;
176                    public static final int SET_RANK_VALUE             =  21;
177                    public static final int STARTED_LIVE_SESSION_VALUE =  30;
178                    public static final int ENDED_LIVE_SESSION_VALUE   =  39;
179                    public static final int REQUESTED_AUTH_VALUE       =  50;
180                    public static final int GRANTED_AUTH_VALUE         =  51;
181                    public static final int BLOCKED_VALUE              =  53;
182                    public static final int POSTED_TEXT_VALUE          =  61;
183                    public static final int POSTED_EMOTE_VALUE         =  60;
184                    public static final int POSTED_CONTACTS_VALUE      =  63;
185                    public static final int POSTED_SMS_VALUE           =  64;
186                    public static final int POSTED_ALERT_VALUE         =  65;
187                    public static final int POSTED_VOICE_MESSAGE_VALUE =  67;
188                    public static final int POSTED_FILES_VALUE         =  68;
189                    public static final int POSTED_INVOICE_VALUE       =  69;
190                    public static final int HAS_BIRTHDAY_VALUE         = 110;
191            }
192            public enum SendingStatus implements EnumConverting {
193                    /** Message has not been delivered to at least one of the participants  */
194                    SENDING       (1),
195                    /** Message has been delivered to at least one other participant  */
196                    SENT          (2),
197                    /** Message could not be delivered (for SMS this reflects the actual SMS, not the chat message)  */
198                    FAILED_TO_SEND(3);
199                    private final int key;
200                    SendingStatus(int key) {
201                            this.key = key;
202                    };
203                    public int getId()   { return key; }
204                    public EnumConverting getDefault() { return SENDING; }
205                    public EnumConverting convert(int from) { return SendingStatus.get(from); }
206                    public EnumConverting[] getArray(final int size) { return new SendingStatus[size]; }
207                    public static SendingStatus get(int from) {
208                            switch (from) {
209                            case 1: return SENDING;
210                            case 2: return SENT;
211                            case 3: return FAILED_TO_SEND;
212                            }
213                            return SENDING;
214                    }
215                    public static final int SENDING_VALUE        = 1;
216                    public static final int SENT_VALUE           = 2;
217                    public static final int FAILED_TO_SEND_VALUE = 3;
218            }
219            /** Indicates if a message has been consumed (meaning read) or not */
220            public enum ConsumptionStatus implements EnumConverting {
221                    /** Message has been read. Note that this is a read-only property. Consumption status of individual messages can not be set selectively. Message consumption status is determined at the conversation level, based conversation consumption horizon and individual message timestamps. Conversation consumption horizon can be updated with Conversation.SetConsumedHorizon method.   */
222                    CONSUMED             (0),
223                    /** Do not notify the user that they have this unread message  */
224                    UNCONSUMED_SUPPRESSED(1),
225                    /** Notify the user that they have this unread message  */
226                    UNCONSUMED_NORMAL    (2),
227                    /** This message consumption state is marked as DEPRECATED  */
228                    UNCONSUMED_ELEVATED  (3);
229                    private final int key;
230                    ConsumptionStatus(int key) {
231                            this.key = key;
232                    };
233                    public int getId()   { return key; }
234                    public EnumConverting getDefault() { return CONSUMED; }
235                    public EnumConverting convert(int from) { return ConsumptionStatus.get(from); }
236                    public EnumConverting[] getArray(final int size) { return new ConsumptionStatus[size]; }
237                    public static ConsumptionStatus get(int from) {
238                            switch (from) {
239                            case 0: return CONSUMED;
240                            case 1: return UNCONSUMED_SUPPRESSED;
241                            case 2: return UNCONSUMED_NORMAL;
242                            case 3: return UNCONSUMED_ELEVATED;
243                            }
244                            return CONSUMED;
245                    }
246                    public static final int CONSUMED_VALUE              = 0;
247                    public static final int UNCONSUMED_SUPPRESSED_VALUE = 1;
248                    public static final int UNCONSUMED_NORMAL_VALUE     = 2;
249                    public static final int UNCONSUMED_ELEVATED_VALUE   = 3;
250            }
251            /**
252             * For messages of type SET_METADATA that alert participants to changes to the associated Conversation's metadata, indicates which metadata property changed and its P_BODY_XML property contains the changed data. Your UI is expected to detect messages with PARAM_KEY set and to update its visual representation of Conversation accordingly. 
253             * You can use the associated Conversation's properties and methods to obtain the updated metadata rather than parse the message body XML, for example, Conversation.P_META_PICTURE and Conversation.Conversation.GetPropMetaPicture. 
254             */
255            public enum SetMetadataKey implements EnumConverting {
256                    /** Notification message that conversation name has changed.  */
257                    SET_META_NAME      (3640),
258                    /** Notification message that conversation topic has changed.  */
259                    SET_META_TOPIC     (3644),
260                    /** Notification message that conversation guidelines have changed.  */
261                    SET_META_GUIDELINES(3652),
262                    /** Notification message that conversation picture has changed.  */
263                    SET_META_PICTURE   (3658);
264                    private final int key;
265                    SetMetadataKey(int key) {
266                            this.key = key;
267                    };
268                    public int getId()   { return key; }
269                    public EnumConverting getDefault() { return SET_META_NAME; }
270                    public EnumConverting convert(int from) { return SetMetadataKey.get(from); }
271                    public EnumConverting[] getArray(final int size) { return new SetMetadataKey[size]; }
272                    public static SetMetadataKey get(int from) {
273                            switch (from) {
274                            case 3640: return SET_META_NAME;
275                            case 3644: return SET_META_TOPIC;
276                            case 3652: return SET_META_GUIDELINES;
277                            case 3658: return SET_META_PICTURE;
278                            }
279                            return SET_META_NAME;
280                    }
281                    public static final int SET_META_NAME_VALUE       = 3640;
282                    public static final int SET_META_TOPIC_VALUE      = 3644;
283                    public static final int SET_META_GUIDELINES_VALUE = 3652;
284                    public static final int SET_META_PICTURE_VALUE    = 3658;
285            }
286            /** Indicates the reason a user could not join or left a Conversation. SkypeKit automatically sets "could not join"-related values. "Left voluntarily"-related values are set as a result of explicit user actions.  */
287            public enum Leavereason implements EnumConverting {
288                    /** User cannot chat (user is currently logged in with a client that has chat disabled - see Contact.CAPABILITY.CAPABILITY_TEXT)  */
289                    USER_INCAPABLE          (2),
290                    /** Attempt to add local user to a conversation by an unknown contact  */
291                    ADDER_MUST_BE_FRIEND    (3),
292                    /** Attempt to add local user to a conversation by an unauthorized contact  */
293                    ADDER_MUST_BE_AUTHORIZED(4),
294                    /** Local user declined an "invitation" to join a chat  */
295                    DECLINE_ADD             (5),
296                    /** User decided to end participation in an on-going multi-chat  */
297                    UNSUBSCRIBE             (6);
298                    private final int key;
299                    Leavereason(int key) {
300                            this.key = key;
301                    };
302                    public int getId()   { return key; }
303                    public EnumConverting getDefault() { return USER_INCAPABLE; }
304                    public EnumConverting convert(int from) { return Leavereason.get(from); }
305                    public EnumConverting[] getArray(final int size) { return new Leavereason[size]; }
306                    public static Leavereason get(int from) {
307                            switch (from) {
308                            case 2: return USER_INCAPABLE;
309                            case 3: return ADDER_MUST_BE_FRIEND;
310                            case 4: return ADDER_MUST_BE_AUTHORIZED;
311                            case 5: return DECLINE_ADD;
312                            case 6: return UNSUBSCRIBE;
313                            }
314                            return USER_INCAPABLE;
315                    }
316                    public static final int USER_INCAPABLE_VALUE           = 2;
317                    public static final int ADDER_MUST_BE_FRIEND_VALUE     = 3;             public static final int ADDER_MUST_BE_AUTHORIZED_VALUE = 4;
318                    public static final int DECLINE_ADD_VALUE              = 5;
319                    public static final int UNSUBSCRIBE_VALUE              = 6;
320            }
321            private final static byte[] P_CONVERSATION_req = {(byte) 90,(byte) 71,(byte) 192,(byte) 7,(byte) 93,(byte) 9};
322            private final static byte[] P_CONVO_GUID_req = {(byte) 90,(byte) 71,(byte) 120,(byte) 93,(byte) 9};
323            private final static byte[] P_AUTHOR_req = {(byte) 90,(byte) 71,(byte) 122,(byte) 93,(byte) 9};
324            private final static byte[] P_AUTHOR_DISPLAY_NAME_req = {(byte) 90,(byte) 71,(byte) 123,(byte) 93,(byte) 9};
325            private final static byte[] P_GUID_req = {(byte) 90,(byte) 71,(byte) 152,(byte) 6,(byte) 93,(byte) 9};
326            private final static byte[] P_ORIGINALLY_MEANT_FOR_req = {(byte) 90,(byte) 71,(byte) 150,(byte) 6,(byte) 93,(byte) 9};
327            private final static byte[] P_TIMESTAMP_req = {(byte) 90,(byte) 71,(byte) 121,(byte) 93,(byte) 9};
328            private final static byte[] P_TYPE_req = {(byte) 90,(byte) 71,(byte) 193,(byte) 7,(byte) 93,(byte) 9};
329            private final static byte[] P_SENDING_STATUS_req = {(byte) 90,(byte) 71,(byte) 194,(byte) 7,(byte) 93,(byte) 9};
330            private final static byte[] P_CONSUMPTION_STATUS_req = {(byte) 90,(byte) 71,(byte) 200,(byte) 7,(byte) 93,(byte) 9};
331            private final static byte[] P_EDITED_BY_req = {(byte) 90,(byte) 71,(byte) 222,(byte) 1,(byte) 93,(byte) 9};
332            private final static byte[] P_EDIT_TIMESTAMP_req = {(byte) 90,(byte) 71,(byte) 223,(byte) 1,(byte) 93,(byte) 9};
333            private final static byte[] P_PARAM_KEY_req = {(byte) 90,(byte) 71,(byte) 195,(byte) 7,(byte) 93,(byte) 9};
334            private final static byte[] P_PARAM_VALUE_req = {(byte) 90,(byte) 71,(byte) 196,(byte) 7,(byte) 93,(byte) 9};
335            private final static byte[] P_BODY_XML_req = {(byte) 90,(byte) 71,(byte) 127,(byte) 93,(byte) 9};
336            private final static byte[] P_IDENTITIES_req = {(byte) 90,(byte) 71,(byte) 125,(byte) 93,(byte) 9};
337            private final static byte[] P_REASON_req = {(byte) 90,(byte) 71,(byte) 198,(byte) 7,(byte) 93,(byte) 9};
338            private final static byte[] P_LEAVEREASON_req = {(byte) 90,(byte) 71,(byte) 126,(byte) 93,(byte) 9};
339            private final static byte[] P_PARTICIPANT_COUNT_req = {(byte) 90,(byte) 71,(byte) 214,(byte) 7,(byte) 93,(byte) 9};
340            /** Properties of the Message class */
341            public enum Property implements PropertyEnumConverting {
342                    P_UNKNOWN             (0,0,null,0,null),
343                    P_CONVERSATION        (960, 1, P_CONVERSATION_req, 18, null),
344                    P_CONVO_GUID          (120, 2, P_CONVO_GUID_req, 0, null),
345                    P_AUTHOR              (122, 3, P_AUTHOR_req, 0, null),
346                    P_AUTHOR_DISPLAY_NAME (123, 4, P_AUTHOR_DISPLAY_NAME_req, 0, null),
347                    P_GUID                (792, 5, P_GUID_req, 0, null),
348                    P_ORIGINALLY_MEANT_FOR(790, 6, P_ORIGINALLY_MEANT_FOR_req, 0, null),
349                    P_TIMESTAMP           (121, 7, P_TIMESTAMP_req, 0, null),
350                    P_TYPE                (961, 8, P_TYPE_req, 0, Type.get(0)),
351                    P_SENDING_STATUS      (962, 9, P_SENDING_STATUS_req, 0, SendingStatus.get(0)),
352                    P_CONSUMPTION_STATUS  (968, 10, P_CONSUMPTION_STATUS_req, 0, ConsumptionStatus.get(0)),
353                    P_EDITED_BY           (222, 11, P_EDITED_BY_req, 0, null),
354                    P_EDIT_TIMESTAMP      (223, 12, P_EDIT_TIMESTAMP_req, 0, null),
355                    P_PARAM_KEY           (963, 13, P_PARAM_KEY_req, 0, null),
356                    P_PARAM_VALUE         (964, 14, P_PARAM_VALUE_req, 0, null),
357                    P_BODY_XML            (127, 15, P_BODY_XML_req, 0, null),
358                    P_IDENTITIES          (125, 16, P_IDENTITIES_req, 0, null),
359                    P_REASON              (966, 17, P_REASON_req, 0, null),
360                    P_LEAVEREASON         (126, 18, P_LEAVEREASON_req, 0, Skype.LeaveReason.get(0)),
361                    P_PARTICIPANT_COUNT   (982, 19, P_PARTICIPANT_COUNT_req, 0, null);
362                    private final int    key;
363                    private final int    idx;
364                    private final byte[] req;
365                    private final int    mod;
366                    private final EnumConverting enumConverter;
367                    Property(int key, int idx, byte[] req, int mod, EnumConverting converter) {
368                            this.key = key;
369                            this.idx = idx;
370                            this.req = req;
371                            this.mod = mod;
372                            this.enumConverter = converter;
373                    };
374                    public boolean  isCached()    { return idx > 0;   }
375                    public int      getIdx()      { return idx;       }
376                    public int      getId()       { return key;       }
377                    public byte[]   getRequest()  { return req;       }
378                    public EnumConverting getDefault()  { return P_UNKNOWN; }
379                    public int      getModuleId() { return mod;       }
380                    public EnumConverting getEnumConverter()    { return enumConverter;   }
381                    public EnumConverting convert(final int from) { return Property.get(from); }
382                    public EnumConverting[] getArray(final int size) { return new Property[size]; }
383                    public static Property get(final int from) {
384                            switch (from) {
385                            case 960: return P_CONVERSATION;
386                            case 120: return P_CONVO_GUID;
387                            case 122: return P_AUTHOR;
388                            case 123: return P_AUTHOR_DISPLAY_NAME;
389                            case 792: return P_GUID;
390                            case 790: return P_ORIGINALLY_MEANT_FOR;
391                            case 121: return P_TIMESTAMP;
392                            case 961: return P_TYPE;
393                            case 962: return P_SENDING_STATUS;
394                            case 968: return P_CONSUMPTION_STATUS;
395                            case 222: return P_EDITED_BY;
396                            case 223: return P_EDIT_TIMESTAMP;
397                            case 963: return P_PARAM_KEY;
398                            case 964: return P_PARAM_VALUE;
399                            case 127: return P_BODY_XML;
400                            case 125: return P_IDENTITIES;
401                            case 966: return P_REASON;
402                            case 126: return P_LEAVEREASON;
403                            case 982: return P_PARTICIPANT_COUNT;
404                            }
405                            return P_UNKNOWN;
406                    }
407                    public static final int P_CONVERSATION_VALUE         = 960;
408                    public static final int P_CONVO_GUID_VALUE           = 120;
409                    public static final int P_AUTHOR_VALUE               = 122;
410                    public static final int P_AUTHOR_DISPLAY_NAME_VALUE  = 123;
411                    public static final int P_GUID_VALUE                 = 792;
412                    public static final int P_ORIGINALLY_MEANT_FOR_VALUE = 790;
413                    public static final int P_TIMESTAMP_VALUE            = 121;
414                    public static final int P_TYPE_VALUE                 = 961;
415                    public static final int P_SENDING_STATUS_VALUE       = 962;
416                    public static final int P_CONSUMPTION_STATUS_VALUE   = 968;
417                    public static final int P_EDITED_BY_VALUE            = 222;
418                    public static final int P_EDIT_TIMESTAMP_VALUE       = 223;
419                    public static final int P_PARAM_KEY_VALUE            = 963;
420                    public static final int P_PARAM_VALUE_VALUE          = 964;
421                    public static final int P_BODY_XML_VALUE             = 127;
422                    public static final int P_IDENTITIES_VALUE           = 125;
423                    public static final int P_REASON_VALUE               = 966;
424                    public static final int P_LEAVEREASON_VALUE          = 126;
425                    public static final int P_PARTICIPANT_COUNT_VALUE    = 982;
426                    public static final Property[] mget_info_mreq = { P_CONVERSATION, P_AUTHOR_DISPLAY_NAME, P_TYPE, P_BODY_XML, P_TIMESTAMP };
427            }
428            private final static byte[] canEdit_req = {(byte) 90,(byte) 82,(byte) 9,(byte) 1};
429            /** For Message types having a body, determines whether that body is editable by the user.   * @return result
430             */
431            public boolean canEdit() {
432                    try {
433                            return sidDoRequest(canEdit_req)
434                            .endRequest().getBoolParm(1, true);
435                    } catch(IOException e) {
436                            mSidRoot.sidOnFatalError(e);
437                            return false
438                    ;}
439            }
440            private final static byte[] edit_req = {(byte) 90,(byte) 82,(byte) 9,(byte) 2};
441            /**
442             * For Message types that include a body and are editable:  
443             *  - alters BODY_XML of the message object 
444             *  - sets EDITED_BY and EDIT_TIMESTAMP properties  
445             *  - propagates the changes to remote users. 
446             * @param newText New value of the message BODY_XML property. 
447             * @param isXml Specify isXML as true if the message body is formatted as XML; omit it or specify it as false if the message body is plain text. 
448             * @param undo Reverts the message body to the original version. newText parameter is ignored when this is set
449             */
450            public void edit(String newText, boolean isXml, boolean undo) {
451                    try {
452                            sidDoRequest(edit_req)
453                            .addStringParm(1, newText)
454                            .addBoolParm(2, isXml)
455                            .addBoolParm(3, undo)
456                            .endOneWay();
457                    } catch(IOException e) {
458                            mSidRoot.sidOnFatalError(e);
459                    }
460            }
461            private final static byte[] getContacts_req = {(byte) 90,(byte) 82,(byte) 9,(byte) 3};
462            /** For messages of type POSTED_CONTACTS, parses the body XML and formats the data as a list of Contact instances.  * @return contacts
463             */
464            public Contact[] getContacts() {
465                    try {
466                            return (Contact[]) sidDoRequest(getContacts_req)                        .endRequest().getObjectListParm(1, 2, true);
467                    } catch(IOException e) {
468                            mSidRoot.sidOnFatalError(e);
469                            return null
470                    ;}
471            }
472            private final static byte[] getTransfers_req = {(byte) 90,(byte) 82,(byte) 9,(byte) 4};
473            /** For messages of type POSTED_FILES, parses the body XML and creates a list of Transfer instances.  * @return transfers
474             */
475            public Transfer[] getTransfers() {
476                    try {
477                            return (Transfer[]) sidDoRequest(getTransfers_req)
478                            .endRequest().getObjectListParm(1, 6, true);
479                    } catch(IOException e) {
480                            mSidRoot.sidOnFatalError(e);
481                            return null
482                    ;}
483            }
484            private final static byte[] getVoiceMessage_req = {(byte) 90,(byte) 82,(byte) 9,(byte) 5};
485            /** For messages of type POSTED_VOICE_MESSAGE, parses the body XML and creates a Voicemail instance.  * @return voicemail
486             */
487            public Voicemail getVoiceMessage() {
488                    try {
489                            return (Voicemail) sidDoRequest(getVoiceMessage_req)
490                            .endRequest().getObjectParm(1, 7, true);
491                    } catch(IOException e) {
492                            mSidRoot.sidOnFatalError(e);
493                            return null
494                    ;}
495            }
496            private final static byte[] getSms_req = {(byte) 90,(byte) 82,(byte) 9,(byte) 6};
497            /** For messages of type POSTED_SMS, parses the body XML and creates an SMS instances  * @return sms
498             */
499            public Sms getSms() {
500                    try {
501                            return (Sms) sidDoRequest(getSms_req)
502                            .endRequest().getObjectParm(1, 12, true);
503                    } catch(IOException e) {
504                            mSidRoot.sidOnFatalError(e);
505                            return null
506                    ;}
507            }
508            private final static byte[] deleteLocally_req = {(byte) 90,(byte) 82,(byte) 9,(byte) 8};
509            /** Deletes this message from the local database. These deletions do not propagate to the other Skype instances that the user may have on other computers. Nor do they affect other participants that have the same message. This method is specifically from removing Message objects from the database - not for removing Messages from conversations. To remove a Message from a conversation, use Message.Edit method to replace the existing body text with an empty string.  */
510            public void deleteLocally() {
511                    try {
512                            sidDoRequest(deleteLocally_req)
513                            .endOneWay();
514                    } catch(IOException e) {
515                            mSidRoot.sidOnFatalError(e);
516                    }
517            }
518            /***
519             * generic multiget of a list of Property
520             * @param requested the list of requested properties of Message
521             * @return SidGetResponding
522             */
523            public SidGetResponding sidMultiGet(Property[] requested) {
524                    return super.sidMultiGet(requested);
525            }
526            /***
527             * generic multiget of list of Property for a list of Message
528             * @param requested the list of requested properties
529             * @return SidGetResponding[] can be casted to (Message[]) if all properties are cached
530             */
531            static public SidGetResponding[] sidMultiGet(Property[] requested, Message[] objects) {
532                    return SidObject.sidMultiGet(requested, objects);
533            }
534            /*** multiget the following properties
535             * - P_CONVERSATION
536             * - P_AUTHOR_DISPLAY_NAME
537             * - P_TYPE
538             * - P_BODY_XML
539             * - P_TIMESTAMP
540             */
541            public Message mgetInfo() {
542                    return (Message) super.sidMultiGet(Property.mget_info_mreq, this);
543            }
544            /*** multiget the following properties for a list of Message
545             * - P_CONVERSATION
546             * - P_AUTHOR_DISPLAY_NAME
547             * - P_TYPE
548             * - P_BODY_XML
549             * - P_TIMESTAMP
550             * @param objects targets of the request
551             * @return Message[] responses
552             */
553            static public Message[] mgetInfo(Message[] objects) {
554                    return (Message[]) SidObject.sidMultiGet(Property.mget_info_mreq, objects, objects);
555            }
556            /** DB ID of corresponding conversation */
557            public Conversation getConversation() {
558                    synchronized(this) {
559                            if ((mSidCached & 0x1) != 0)
560                                    return mConversation;
561                    }
562                    return (Conversation) sidRequestObjectProperty(Property.P_CONVERSATION);
563            }
564            /** GUID of the Conversation. The GUID is a "global ID" - these values are shared accross Skype client instances and accross all the participants of the conversation.  */
565            public String getConvoGuid() {
566                    synchronized(this) {
567                            if ((mSidCached & 0x2) != 0)
568                                    return mConvoGuid;
569                    }
570                    return sidRequestStringProperty(Property.P_CONVO_GUID);
571            }
572            /** Identity of the sender. While this is almost always the same as SKYPENAME property of the Contact, in some rare cases it can also be a phone number - for example, incoming voicemail notification Messages (message type = POSTED_VOICE_MESSAGE).  */
573            public String getAuthor() {
574                    synchronized(this) {
575                            if ((mSidCached & 0x4) != 0)
576                                    return mAuthor;
577                    }
578                    return sidRequestStringProperty(Property.P_AUTHOR);
579            }
580            /** displayname of the sender at the time of posting */
581            public String getAuthorDisplayName() {
582                    synchronized(this) {
583                            if ((mSidCached & 0x8) != 0)
584                                    return mAuthorDisplayName;
585                    }
586                    return sidRequestStringProperty(Property.P_AUTHOR_DISPLAY_NAME);
587            }
588            /** Unlike the message id, the GUID is the same on all instances and for all participants.  */
589            public byte[] getGuid() {
590                    synchronized(this) {
591                            if ((mSidCached & 0x10) != 0)
592                                    return mGuid;
593                    }
594                    return sidRequestBinaryProperty(Property.P_GUID);
595            }
596            /** This property gets set when a conference is spawned from dialog Conversation. In that case recent message history from the original dialog is copied to the target conversation. For all the copied messages, the ORIGINALLY_MEANT_FOR property will be set to identity of the remote participant of the original dialog.  */
597            public String getOriginallyMeantFor() {
598                    synchronized(this) {
599                            if ((mSidCached & 0x20) != 0)
600                                    return mOriginallyMeantFor;
601                    }
602                    return sidRequestStringProperty(Property.P_ORIGINALLY_MEANT_FOR);
603            }
604            /** UNIX timestamp (sent time, adjusted for local clock) */
605            public int getTimestamp() {
606                    synchronized(this) {
607                            if ((mSidCached & 0x40) != 0)
608                                    return mTimestamp;
609                    }
610                    return sidRequestUintProperty(Property.P_TIMESTAMP);
611            }
612            public Type getType() {
613                    synchronized(this) {
614                            if ((mSidCached & 0x80) != 0)
615                                    return mType;
616                    }
617                    return (Type) sidRequestEnumProperty(Property.P_TYPE);
618            }
619            public SendingStatus getSendingStatus() {
620                    synchronized(this) {
621                            if ((mSidCached & 0x100) != 0)
622                                    return mSendingStatus;
623                    }
624                    return (SendingStatus) sidRequestEnumProperty(Property.P_SENDING_STATUS);
625            }
626            public ConsumptionStatus getConsumptionStatus() {
627                    synchronized(this) {
628                            if ((mSidCached & 0x200) != 0)
629                                    return mConsumptionStatus;
630                    }
631                    return (ConsumptionStatus) sidRequestEnumProperty(Property.P_CONSUMPTION_STATUS);
632            }
633            /** Identity of the author that last edited this message. NULL if message has not been edited  */
634            public String getEditedBy() {
635                    synchronized(this) {
636                            if ((mSidCached & 0x400) != 0)
637                                    return mEditedBy;
638                    }
639                    return sidRequestStringProperty(Property.P_EDITED_BY);
640            }
641            /** UNIX timestamp of last edit */
642            public int getEditTimestamp() {
643                    synchronized(this) {
644                            if ((mSidCached & 0x800) != 0)
645                                    return mEditTimestamp;
646                    }
647                    return sidRequestUintProperty(Property.P_EDIT_TIMESTAMP);
648            }
649            /** Message type-specific parameter. See Message.SET_METADATA_KEY for more information.  */
650            public int getParamKey() {
651                    synchronized(this) {
652                            if ((mSidCached & 0x1000) != 0)
653                                    return mParamKey;
654                    }
655                    return sidRequestUintProperty(Property.P_PARAM_KEY);
656            }
657            /** Message type-specific parameter  */
658            public int getParamValue() {
659                    synchronized(this) {
660                            if ((mSidCached & 0x2000) != 0)
661                                    return mParamValue;
662                    }
663                    return sidRequestUintProperty(Property.P_PARAM_VALUE);
664            }
665            /** Message type-specific parameter  */
666            public String getBodyXml() {
667                    synchronized(this) {
668                            if ((mSidCached & 0x4000) != 0)
669                                    return mBodyXml;
670                    }
671                    return sidRequestXmlProperty(Property.P_BODY_XML);
672            }
673            /**
674             * Message type-specific parameter. Depending of Message type, this property contains: 
675             *  - STARTED_LIVESESSION - list of participants in the cal; 
676             *  - ENDED_LIVESESSION - list of participants in the call; 
677             *  - POSTED_SMS - list of recipients of the message; 
678             *  - SPAWNED_CONFERENCE - the list of identities that were added; 
679             *  - ADDED_CONSUMERS - the list of identities that were added; 
680             *  - RETIRED_OTHERS - the skypename of the participant who was kicked; 
681             *  - SET_RANK - the skypename of the participant whose rank was changed; 
682             *  - REQUESTED_AUTH - Message.P_AUTHOR and Message.P_IDENTITIES are set to the users receiving and requesting the authorization, depending if the message was received or sent; 
683             *  - GRANTED_AUTH - the skypename of the user we granted authorization;         *  - BLOCKED - the skypename of the user who was blocked; 
684             *  - HAS_BIRTHDAY - skypename of current logged in user. 
685             */
686            public String getIdentities() {
687                    synchronized(this) {
688                            if ((mSidCached & 0x8000) != 0)
689                                    return mIdentities;
690                    }
691                    return sidRequestStringProperty(Property.P_IDENTITIES);
692            }
693            /**
694             * Message type-specific parameter. Possible values for STARTED/ENDED_LIVESESSION (only set for dialogs): 
695             *  - no_answer  
696             *  - manual  
697             *  - busy  
698             *  - connection_dropped 
699             *  - no_skypeout_subscription; 
700             *  - insufficient_funds 
701             *  - internet_connection_lost 
702             *  - skypeout_account_blocked 
703             *  - pstn_could_not_connect_to_skype_proxy 
704             *  - pstn_invalid_number 
705             *  - pstn_number_forbidden 
706             *  - pstn_call_timed_out 
707             *  - pstn_busy 
708             *  - pstn_call_terminated 
709             *  - pstn_network_error 
710             *  - number_unavailable 
711             *  - pstn_call_rejected 
712             *  - pstn_misc_error 
713             *  - internal_error 
714             *  - unable_to_connect 
715             *  - connection_dropped 
716             *  - recording_failed 
717             *  - playback_error 
718             *  - legacy_error 
719             *  - blocked_by_privacy_settings 
720             *  - error 
721             *  - transfer_failed 
722             *  - transfer_insufficient_funds 
723             *  - blocked_by_us 
724             *  - emergency_call_denied 
725             * 
726             * This information is now available as an enum in LEAVEREASON 
727             */
728            public String getReason() {
729                    synchronized(this) {
730                            if ((mSidCached & 0x10000) != 0)
731                                    return mReason;
732                    }
733                    return sidRequestStringProperty(Property.P_REASON);
734            }
735            /** Leave reason for message of the RETIRED type, and STARTED/ENDED_LIVESESSION.                   Use for STARTED/ENDED_LIVESESSION is to provide simpler, enum based                   handling and deprecates the reason property (only set for dialogs) */
736            public Skype.LeaveReason getLeavereason() {
737                    synchronized(this) {
738                            if ((mSidCached & 0x20000) != 0)
739                                    return mLeavereason;
740                    }
741                    return (Skype.LeaveReason) sidRequestEnumProperty(Property.P_LEAVEREASON);
742            }
743            /** Number of people who received this message (including local user)  */
744            public int getParticipantCount() {
745                    synchronized(this) {
746                            if ((mSidCached & 0x40000) != 0)
747                                    return mParticipantCount;
748                    }
749                    return sidRequestUintProperty(Property.P_PARTICIPANT_COUNT);
750            }
751            public String sidGetStringProperty(final PropertyEnumConverting prop) {
752                    switch(prop.getId()) {
753                    case 120:
754                            return mConvoGuid;
755                    case 122:
756                            return mAuthor;
757                    case 123:
758                            return mAuthorDisplayName;
759                    case 790:
760                            return mOriginallyMeantFor;
761                    case 222:
762                            return mEditedBy;
763                    case 127:
764                            return mBodyXml;
765                    case 125:
766                            return mIdentities;
767                    case 966:
768                            return mReason;
769                    }
770                    return "";
771            }
772            public SidObject sidGetObjectProperty(final PropertyEnumConverting prop) {
773                    assert(prop.getId() == 960);
774                    return mConversation;
775            }
776            public int sidGetIntProperty(final PropertyEnumConverting prop) {
777                    switch(prop.getId()) {
778                    case 121:
779                            return mTimestamp;
780                    case 223:
781                            return mEditTimestamp;
782                    case 963:
783                            return mParamKey;
784                    case 964:
785                            return mParamValue;
786                    case 982:
787                            return mParticipantCount;
788                    }
789                    return 0;
790            }
791            public EnumConverting sidGetEnumProperty(final PropertyEnumConverting prop) {
792                    switch(prop.getId()) {
793                    case 961:
794                            return mType;
795                    case 962:
796                            return mSendingStatus;
797                    case 968:
798                            return mConsumptionStatus;
799                    case 126:
800                            return mLeavereason;
801                    }
802                    return null;
803            }
804            public byte[] sidGetBinaryProperty(final PropertyEnumConverting prop) {
805                    assert(prop.getId() == 792);
806                    return mGuid;
807            }
808            public String getPropertyAsString(final int prop) {
809                    switch (prop) {
810                    case 960: return Integer.toString(getConversation().getOid());
811                    case 120: return getConvoGuid();
812                    case 122: return getAuthor();
813                    case 123: return getAuthorDisplayName();
814                    case 792: return "<binary>";
815                    case 790: return getOriginallyMeantFor();
816                    case 121: return Integer.toString(getTimestamp());
817                    case 961: return getType().toString();
818                    case 962: return getSendingStatus().toString();
819                    case 968: return getConsumptionStatus().toString();
820                    case 222: return getEditedBy();
821                    case 223: return Integer.toString(getEditTimestamp());
822                    case 963: return Integer.toString(getParamKey());
823                    case 964: return Integer.toString(getParamValue());
824                    case 125: return getIdentities();
825                    case 966: return getReason();
826                    case 126: return getLeavereason().toString();
827                    case 982: return Integer.toString(getParticipantCount());
828                    }
829                    return "<unkown>";
830            }
831            public String getPropertyAsString(final Property prop) {
832                    return getPropertyAsString(prop.getId());
833            }
834            protected void sidOnChangedProperty(final int propertyId, final int value, final String svalue) {
835                    final Property property = Property.get(propertyId);
836                    if (property == Property.P_UNKNOWN) return;
837                    final int idx = property.getIdx();
838                    if (idx != 0) {
839                            int bit  = 1<<((idx-1)%32);
840                            synchronized (this) {
841                                    mSidCached|=bit;
842                                    switch (propertyId) {
843                                    case 120:
844                                            if (svalue != null) mConvoGuid = svalue;
845                                            else mSidCached &=~bit;
846                                            break;
847                                    case 122:
848                                            if (svalue != null) mAuthor = svalue;
849                                            else mSidCached &=~bit;
850                                            break;
851                                    case 123:
852                                            if (svalue != null) mAuthorDisplayName = svalue;
853                                            else mSidCached &=~bit;
854                                            break;
855                                    case 790:
856                                            if (svalue != null) mOriginallyMeantFor = svalue;
857                                            else mSidCached &=~bit;
858                                            break;
859                                    case 121: mTimestamp = value; break;
860                                    case 961: mType = Type.get(value); break;
861                                    case 962: mSendingStatus = SendingStatus.get(value); break;
862                                    case 968: mConsumptionStatus = ConsumptionStatus.get(value); break;
863                                    case 222:
864                                            if (svalue != null) mEditedBy = svalue;
865                                            else mSidCached &=~bit;
866                                            break;
867                                    case 223: mEditTimestamp = value; break;
868                                    case 963: mParamKey = value; break;
869                                    case 964: mParamValue = value; break;
870                                    case 125:
871                                            if (svalue != null) mIdentities = svalue;
872                                            else mSidCached &=~bit;
873                                            break;
874                                    case 966:
875                                            if (svalue != null) mReason = svalue;
876                                            else mSidCached &=~bit;
877                                            break;
878                                    case 126: mLeavereason = Skype.LeaveReason.get(value); break;
879                                    case 982: mParticipantCount = value; break;
880                                    default: mSidCached|=bit; break;
881                                    }
882                            }
883                    }
884                    MessageListener listener = ((Skype) mSidRoot).getMessageListener();
885                    if (listener != null)
886                            listener.onPropertyChange(this, property, value, svalue);
887            }
888            public void sidSetProperty(final PropertyEnumConverting prop, final String newValue) {
889                    final int propId = prop.getId();
890                    switch(propId) {
891                    case 120:
892                            mSidCached |= 0x2;
893                            mConvoGuid=  newValue;
894                            break;
895                    case 122:
896                            mSidCached |= 0x4;
897                            mAuthor=  newValue;
898                            break;
899                    case 123:
900                            mSidCached |= 0x8;
901                            mAuthorDisplayName=  newValue;
902                            break;
903                    case 790:
904                            mSidCached |= 0x20;
905                            mOriginallyMeantFor=  newValue;
906                            break;
907                    case 222:
908                            mSidCached |= 0x400;
909                            mEditedBy=  newValue;
910                            break;
911                    case 127:
912                            mSidCached |= 0x4000;
913                            mBodyXml=  newValue;
914                            break;
915                    case 125:
916                            mSidCached |= 0x8000;
917                            mIdentities=  newValue;
918                            break;
919                    case 966:
920                            mSidCached |= 0x10000;
921                            mReason=  newValue;
922                            break;
923                    }
924            }
925            public void sidSetProperty(final PropertyEnumConverting prop, final SidObject newValue) {
926                    final int propId = prop.getId();
927                    assert(propId == 960);
928                    mSidCached |= 0x1;
929                    mConversation= (Conversation) newValue;
930            }
931            public void sidSetProperty(final PropertyEnumConverting prop, final int newValue) {
932                    final int propId = prop.getId();
933                    switch(propId) {
934                    case 121:
935                            mSidCached |= 0x40;
936                            mTimestamp=  newValue;
937                            break;
938                    case 961:
939                            mSidCached |= 0x80;
940                            mType= Type.get(newValue);
941                            break;
942                    case 962:
943                            mSidCached |= 0x100;
944                            mSendingStatus= SendingStatus.get(newValue);
945                            break;
946                    case 968:
947                            mSidCached |= 0x200;
948                            mConsumptionStatus= ConsumptionStatus.get(newValue);
949                            break;
950                    case 223:
951                            mSidCached |= 0x800;
952                            mEditTimestamp=  newValue;
953                            break;
954                    case 963:
955                            mSidCached |= 0x1000;
956                            mParamKey=  newValue;
957                            break;
958                    case 964:
959                            mSidCached |= 0x2000;
960                            mParamValue=  newValue;
961                            break;
962                    case 126:
963                            mSidCached |= 0x20000;
964                            mLeavereason= Skype.LeaveReason.get(newValue);
965                            break;
966                    case 982:
967                            mSidCached |= 0x40000;
968                            mParticipantCount=  newValue;
969                            break;
970                    }
971            }
972            public void sidSetProperty(final PropertyEnumConverting prop, final byte[] newValue) {
973                    final int propId = prop.getId();
974                    assert(propId == 792);
975                    mSidCached |= 0x10;             mGuid=  newValue;
976            }
977            public Conversation      mConversation;
978            public String            mConvoGuid;
979            public String            mAuthor;
980            public String            mAuthorDisplayName;
981            public byte[]            mGuid;
982            public String            mOriginallyMeantFor;
983            public int               mTimestamp;
984            public Type              mType;
985            public SendingStatus     mSendingStatus;
986            public ConsumptionStatus mConsumptionStatus;
987            public String            mEditedBy;
988            public int               mEditTimestamp;
989            public int               mParamKey;
990            public int               mParamValue;
991            public String            mBodyXml;
992            public String            mIdentities;
993            public String            mReason;
994            public Skype.LeaveReason mLeavereason;
995            public int               mParticipantCount;
996            public int moduleId() {
997                    return 9;
998            }
999            
1000            public Message(final int oid, final SidRoot root) {
1001                    super(oid, root, 19);
1002            }
1003    }