1 /*
2     Kopete Groupwise Protocol
3     logintask.cpp - Send our credentials to the server and process the contact list and privacy details that it returns.
4 
5     Copyright (c) 2006      Novell, Inc	         http://www.opensuse.org
6     Copyright (c) 2004      SUSE Linux AG	     http://www.suse.com
7 
8     Based on Iris, Copyright (C) 2003  Justin Karneges <justin@affinix.com>
9 
10     Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
11 
12     *************************************************************************
13     *                                                                       *
14     * This library is free software; you can redistribute it and/or         *
15     * modify it under the terms of the GNU Lesser General Public            *
16     * License as published by the Free Software Foundation; either          *
17     * version 2 of the License, or (at your option) any later version.      *
18     *                                                                       *
19     *************************************************************************
20 */
21 
22 #include "logintask.h"
23 #include "client.h"
24 #include "response.h"
25 #include "privacymanager.h"
26 #include "userdetailsmanager.h"
27 
28 #include <QByteArray>
29 
LoginTask(Task * parent)30 LoginTask::LoginTask(Task *parent)
31     : RequestTask(parent)
32 {
33 }
34 
~LoginTask()35 LoginTask::~LoginTask()
36 {
37 }
38 
initialise()39 void LoginTask::initialise()
40 {
41     QString command = QStringLiteral("login:%1:%2").arg(client()->host()).arg(client()->port());
42 
43     Field::FieldList lst;
44     lst.append(new Field::SingleField(Field::NM_A_SZ_USERID, 0, NMFIELD_TYPE_UTF8, client()->userId()));
45     lst.append(new Field::SingleField(Field::NM_A_SZ_CREDENTIALS, 0, NMFIELD_TYPE_UTF8, client()->password()));
46     lst.append(new Field::SingleField(Field::NM_A_SZ_USER_AGENT, 0, NMFIELD_TYPE_UTF8, client()->userAgent()));
47     lst.append(new Field::SingleField(Field::NM_A_UD_BUILD, 0, NMFIELD_TYPE_UDWORD, client()->protocolVersion()));
48     lst.append(new Field::SingleField(Field::NM_A_IP_ADDRESS, 0, NMFIELD_TYPE_UTF8, client()->ipAddress()));
49     createTransfer(command, lst);
50 }
51 
take(Transfer * transfer)52 bool LoginTask::take(Transfer *transfer)
53 {
54     if (!forMe(transfer)) {
55         return false;
56     }
57     Response *response = dynamic_cast<Response *>(transfer);
58     if (!response) {
59         return false;
60     }
61     if (response->resultCode()) {
62         setError(response->resultCode());
63         return true;
64     }
65     response->fields().dump(true);
66 
67     // read in myself()'s metadata fields and emit signal
68     Field::FieldList loginResponseFields = response->fields();
69 
70     ContactDetails cd = extractUserDetails(loginResponseFields);
71     emit gotMyself(cd);
72 
73     // read the privacy settings first, because this affects all contacts' apparent status
74     extractPrivacy(loginResponseFields);
75 
76     extractCustomStatuses(loginResponseFields);
77 
78     // CREATE CONTACT LIST
79     // locate contact list
80     Field::MultiField *contactList = loginResponseFields.findMultiField(Field::NM_A_FA_CONTACT_LIST);
81     if (contactList) {
82         Field::FieldList contactListFields = contactList->fields();
83         Field::MultiField *container;
84         // read folders
85         for (Field::FieldListIterator it = contactListFields.find(Field::NM_A_FA_FOLDER);
86              it != contactListFields.end();
87              it = contactListFields.find(++it, Field::NM_A_FA_FOLDER)) {
88             container = static_cast<Field::MultiField *>(*it);
89             extractFolder(container);
90         }
91 
92         // read contacts
93         for (Field::FieldListIterator it = contactListFields.find(Field::NM_A_FA_CONTACT);
94              it != contactListFields.end();
95              it = contactListFields.find(++it, Field::NM_A_FA_CONTACT)) {
96             container = static_cast<Field::MultiField *>(*it);
97             extractContact(container);
98         }
99     }
100 
101     extractKeepalivePeriod(loginResponseFields);
102 
103     setSuccess();
104 
105     return true;
106 }
107 
extractFolder(Field::MultiField * folderContainer)108 void LoginTask::extractFolder(Field::MultiField *folderContainer)
109 {
110     FolderItem folder;
111     Field::SingleField *current;
112     Field::FieldList fl = folderContainer->fields();
113     // object id
114     current = fl.findSingleField(Field::NM_A_SZ_OBJECT_ID);
115     folder.id = current->value().toInt();
116     // sequence number
117     current = fl.findSingleField(Field::NM_A_SZ_SEQUENCE_NUMBER);
118     folder.sequence = current->value().toInt();
119     // name
120     current = fl.findSingleField(Field::NM_A_SZ_DISPLAY_NAME);
121     folder.name = current->value().toString();
122     // parent
123     current = fl.findSingleField(Field::NM_A_SZ_PARENT_ID);
124     folder.parentId = current->value().toInt();
125 
126     client()->debug(QStringLiteral("Got folder: %1, obj: %2, parent: %3, seq: %4.").arg(folder.name).arg(folder.id).arg(folder.parentId).arg(folder.sequence));
127     // tell the world about it
128     emit gotFolder(folder);
129 }
130 
extractContact(Field::MultiField * contactContainer)131 void LoginTask::extractContact(Field::MultiField *contactContainer)
132 {
133     if (contactContainer->tag() != Field::NM_A_FA_CONTACT) {
134         return;
135     }
136     ContactItem contact;
137     Field::SingleField *current;
138     Field::FieldList fl = contactContainer->fields();
139     // sequence number, object and parent IDs are a numeric values but are stored as strings...
140     current = fl.findSingleField(Field::NM_A_SZ_OBJECT_ID);
141     contact.id = current->value().toInt();
142     current = fl.findSingleField(Field::NM_A_SZ_PARENT_ID);
143     contact.parentId = current->value().toInt();
144     current = fl.findSingleField(Field::NM_A_SZ_SEQUENCE_NUMBER);
145     contact.sequence = current->value().toInt();
146     current = fl.findSingleField(Field::NM_A_SZ_DISPLAY_NAME);
147     contact.displayName = current->value().toString();
148     current = fl.findSingleField(Field::NM_A_SZ_DN);
149     contact.dn = current->value().toString().toLower();
150     emit gotContact(contact);
151     Field::MultiField *details = fl.findMultiField(Field::NM_A_FA_USER_DETAILS);
152     if (details) { // not all contact list contacts have these
153         Field::FieldList detailsFields = details->fields();
154         ContactDetails cd = extractUserDetails(detailsFields);
155         if (cd.dn.isEmpty()) {
156             cd.dn = contact.dn;
157         }
158         // tell the UserDetailsManager that we have this contact's details
159         client()->userDetailsManager()->addDetails(cd);
160         emit gotContactUserDetails(cd);
161     }
162 }
163 
extractUserDetails(Field::FieldList & fields)164 ContactDetails LoginTask::extractUserDetails(Field::FieldList &fields)
165 {
166     ContactDetails cd;
167     cd.status = GroupWise::Invalid;
168     cd.archive = false;
169     // read the supplied fields, set metadata and status.
170     Field::SingleField *sf;
171     if ((sf = fields.findSingleField(Field::NM_A_SZ_AUTH_ATTRIBUTE))) {
172         cd.authAttribute = sf->value().toString();
173     }
174     if ((sf = fields.findSingleField(Field::NM_A_SZ_DN))) {
175         cd.dn = sf->value().toString().toLower(); // HACK: lowercased DN
176     }
177     if ((sf = fields.findSingleField(Field::KOPETE_NM_USER_DETAILS_CN))) {
178         cd.cn = sf->value().toString();
179     }
180     if ((sf = fields.findSingleField(Field::KOPETE_NM_USER_DETAILS_GIVEN_NAME))) {
181         cd.givenName = sf->value().toString();
182     }
183     if ((sf = fields.findSingleField(Field::KOPETE_NM_USER_DETAILS_SURNAME))) {
184         cd.surname = sf->value().toString();
185     }
186     if ((sf = fields.findSingleField(Field::KOPETE_NM_USER_DETAILS_FULL_NAME))) {
187         cd.fullName = sf->value().toString();
188     }
189     if ((sf = fields.findSingleField(Field::KOPETE_NM_USER_DETAILS_ARCHIVE_FLAG))) {
190         cd.archive = (sf->value().toInt() == 1);
191     }
192     if ((sf = fields.findSingleField(Field::NM_A_SZ_STATUS))) {
193         cd.status = sf->value().toInt();
194     }
195     if ((sf = fields.findSingleField(Field::NM_A_SZ_MESSAGE_BODY))) {
196         cd.awayMessage = sf->value().toString();
197     }
198     Field::MultiField *mf;
199     QMap< QString, QVariant > propMap;
200     if ((mf = fields.findMultiField(Field::NM_A_FA_INFO_DISPLAY_ARRAY))) {
201         Field::FieldList fl = mf->fields();
202         const Field::FieldListIterator end = fl.end();
203         for (Field::FieldListIterator it = fl.begin(); it != end; ++it) {
204             Field::SingleField *propField = dynamic_cast<Field::SingleField *>(*it);
205             if (propField) {
206                 QString propName = propField->tag();
207                 QString propValue = propField->value().toString();
208                 propMap.insert(propName, propValue);
209             } else {
210                 Field::MultiField *propList = dynamic_cast<Field::MultiField *>(*it);
211                 if (propList) {
212                     // Hello A Nagappan. GW gave us a multiple field where we previously got a single field
213                     QString parentName = propList->tag();
214                     Field::FieldList propFields = propList->fields();
215                     const Field::FieldListIterator end = propFields.end();
216                     for (Field::FieldListIterator it = propFields.begin(); it != end; ++it) {
217                         propField = dynamic_cast<Field::SingleField *>(*it);
218                         if (propField /*&& propField->tag() == parentName */) {
219                             QString propValue = propField->value().toString();
220                             QString contents = propMap[ propField->tag() ].toString();
221                             if (!contents.isEmpty()) {
222                                 contents.append(", ");
223                             }
224                             contents.append(propField->value().toString());
225                             propMap.insert(propField->tag(), contents);
226                         }
227                     }
228                 }
229             }
230         }
231     }
232     if (!propMap.empty()) {
233         cd.properties = propMap;
234     }
235     return cd;
236 }
237 
extractPrivacy(Field::FieldList & fields)238 void LoginTask::extractPrivacy(Field::FieldList &fields)
239 {
240     bool privacyLocked = false;
241     bool defaultDeny = false;
242     QStringList allowList;
243     QStringList denyList;
244     // read blocking
245     // may be a single field or may be an array
246     Field::FieldListIterator it = fields.find(Field::NM_A_LOCKED_ATTR_LIST);
247     if (it != fields.end()) {
248         if (Field::SingleField *sf = dynamic_cast<Field::SingleField *>(*it)) {
249             if (sf->value().toString().indexOf(Field::NM_A_BLOCKING) != -1) {
250                 privacyLocked = true;
251             }
252         } else if (Field::MultiField *mf = dynamic_cast<Field::MultiField *>(*it)) {
253             Field::FieldList fl = mf->fields();
254             for (Field::FieldListIterator it = fl.begin(); it != fl.end(); ++it) {
255                 if (Field::SingleField *sf = dynamic_cast<Field::SingleField *>(*it)) {
256                     if (sf->tag() == Field::NM_A_BLOCKING) {
257                         privacyLocked = true;
258                         break;
259                     }
260                 }
261             }
262         }
263     }
264 
265     // read default privacy policy
266     Field::SingleField *sf = fields.findSingleField(Field::NM_A_BLOCKING);
267     if (sf) {
268         defaultDeny = (sf->value().toInt() != 0);
269     }
270 
271     // read deny list
272     denyList = readPrivacyItems(Field::NM_A_BLOCKING_DENY_LIST, fields);
273     // read allow list
274     allowList = readPrivacyItems(Field::NM_A_BLOCKING_ALLOW_LIST, fields);
275     emit gotPrivacySettings(privacyLocked, defaultDeny, allowList, denyList);
276 //	qDebug() << "locked is " << privacyLocked << ", default is " << defaultDeny << "\nallow list is: " << allowList << "\ndeny list is: " << denyList;
277 }
278 
readPrivacyItems(const QByteArray & tag,Field::FieldList & fields)279 QStringList LoginTask::readPrivacyItems(const QByteArray &tag, Field::FieldList &fields)
280 {
281     QStringList items;
282 
283     Field::FieldListIterator it = fields.find(tag);
284     if (it != fields.end()) {
285         if (Field::SingleField *sf = dynamic_cast<Field::SingleField *>(*it)) {
286             items.append(sf->value().toString().toLower());
287         } else if (Field::MultiField *mf = dynamic_cast<Field::MultiField *>(*it)) {
288             Field::FieldList fl = mf->fields();
289             for (Field::FieldListIterator it = fl.begin(); it != fl.end(); ++it) {
290                 if (Field::SingleField *sf = dynamic_cast<Field::SingleField *>(*it)) {
291                     items.append(sf->value().toString().toLower());
292                 }
293             }
294         }
295     }
296     return items;
297 }
298 
extractCustomStatuses(Field::FieldList & fields)299 void LoginTask::extractCustomStatuses(Field::FieldList &fields)
300 {
301     Field::FieldListIterator it = fields.find(Field::NM_A_FA_CUSTOM_STATUSES);
302     if (it != fields.end()) {
303         if (Field::MultiField *mf = dynamic_cast<Field::MultiField *>(*it)) {
304             Field::FieldList fl = mf->fields();
305             for (Field::FieldListIterator custStatIt = fl.begin(); custStatIt != fl.end(); ++custStatIt) {
306                 Field::MultiField *mf2 = dynamic_cast<Field::MultiField *>(*custStatIt);
307                 if (mf2 && (mf2->tag() == Field::NM_A_FA_STATUS)) {
308                     GroupWise::CustomStatus custom;
309                     Field::FieldList fl2 = mf2->fields();
310                     for (Field::FieldListIterator custContentIt = fl2.begin(); custContentIt != fl2.end(); ++custContentIt) {
311                         if (Field::SingleField *sf3 = dynamic_cast<Field::SingleField *>(*custContentIt)) {
312                             if (sf3->tag() == Field::NM_A_SZ_TYPE) {
313                                 custom.status = (GroupWise::Status)sf3->value().toInt();
314                             } else if (sf3->tag() == Field::NM_A_SZ_DISPLAY_NAME) {
315                                 custom.name = sf3->value().toString();
316                             } else if (sf3->tag() == Field::NM_A_SZ_MESSAGE_BODY) {
317                                 custom.autoReply = sf3->value().toString();
318                             }
319                         }
320                     }
321                     emit gotCustomStatus(custom);
322                 }
323             }
324         }
325     }
326 }
327 
extractKeepalivePeriod(Field::FieldList & fields)328 void LoginTask::extractKeepalivePeriod(Field::FieldList &fields)
329 {
330     Field::FieldListIterator it = fields.find(Field::NM_A_UD_KEEPALIVE);
331     if (it != fields.end()) {
332         if (Field::SingleField *sf = dynamic_cast<Field::SingleField *>(*it)) {
333             bool ok;
334             int period = sf->value().toInt(&ok);
335             if (ok) {
336                 emit gotKeepalivePeriod(period);
337             }
338         }
339     }
340 }
341