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