1 /*
2 * This file is part of Licq, an instant messaging client for UNIX.
3 * Copyright (C) 2010-2013 Licq developers <licq-dev@googlegroups.com>
4 *
5 * Please refer to the COPYRIGHT file distributed with this source
6 * distribution for the names of the individual contributors.
7 *
8 * Licq is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * Licq is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with Licq; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include "vcard.h"
24
25 #include "user.h"
26
27 #include <gloox/vcard.h>
28
29 #include <licq/crypto.h>
30 #include <licq/logging/log.h>
31
32 #include <cstdlib>
33 #include <cstring>
34 #include <iomanip>
35 #include <sstream>
36
37 using namespace LicqJabber;
38 using Licq::gLog;
39
40 namespace
41 {
guessPictureMimeType(const std::string & bindata)42 static std::string guessPictureMimeType(const std::string& bindata)
43 {
44 if (bindata.size() > 4 && bindata.substr(1, 3) == "PNG")
45 return "image/png";
46
47 if (bindata.size() > 11
48 && (bindata.substr(0, 11).find("JFIF") != std::string::npos
49 || bindata.substr(0, 11).find("Exif") != std::string::npos))
50 return "image/jpeg";
51
52 if (bindata.size() > 3 && bindata.substr(0, 3) == "GIF")
53 return "image/gif";
54
55 return "";
56 }
57
58 } // namespace
59
pictureSha1() const60 boost::optional<std::string> UserToVCard::pictureSha1() const
61 {
62 if (Licq::Sha1::supported())
63 return myUser->pictureSha1();
64 else
65 return boost::none;
66 }
67
createVCard() const68 gloox::VCard* UserToVCard::createVCard() const
69 {
70 gloox::VCard* card = new gloox::VCard;
71
72 card->setJabberid(myUser->accountId());
73 card->setNickname(myUser->getAlias());
74 card->setFormattedname(myUser->getFullName());
75 card->setName(myUser->getLastName(), myUser->getFirstName());
76 if (!myUser->getEmail().empty())
77 card->addEmail(myUser->getEmail(), gloox::VCard::AddrTypePref);
78
79 std::ostringstream tz;
80 int offset = myUser->timezone();
81 if (offset == User::TimezoneUnknown)
82 tz << "-00:00";
83 else
84 {
85 tz << (offset >= 0 ? '+' : '-')
86 << std::setw(2) << std::setfill('0')
87 << std::abs(offset) / 3600
88 << ':'
89 << std::setw(2) << std::setfill('0')
90 << std::abs(offset / 60) % 60;
91 }
92 card->setTz(tz.str());
93
94 if (myUser->GetPicturePresent())
95 {
96 std::string pictureData;
97 if (myUser->readPictureData(pictureData))
98 {
99 // Only 8 KiB allowed according to XEP-0153
100 const size_t maxSize = 8 * 1024;
101
102 if (pictureData.size() < maxSize)
103 card->setPhoto(guessPictureMimeType(pictureData), pictureData);
104 else
105 gLog.error("Picture is too large (%zu bytes); must be less than %zu",
106 pictureData.size(), maxSize);
107 }
108 }
109
110 return card;
111 }
112
113
VCardToUser(const gloox::VCard * vcard)114 VCardToUser::VCardToUser(const gloox::VCard* vcard)
115 : myVCard(vcard)
116 {
117 if (Licq::Sha1::supported())
118 {
119 const gloox::VCard::Photo& photo = myVCard->photo();
120 if (!photo.binval.empty())
121 myPictureSha1 = Licq::Sha1::hashToHexString(photo.binval);
122 }
123 }
124
pictureSha1() const125 boost::optional<std::string> VCardToUser::pictureSha1() const
126 {
127 if (Licq::Sha1::supported())
128 return myPictureSha1;
129 else
130 return boost::none;
131 }
132
updateUser(User * user) const133 int VCardToUser::updateUser(User* user) const
134 {
135 int saveGroup = User::SaveUserInfo;
136 user->SetEnableSave(false);
137
138 if (!user->KeepAliasOnUpdate())
139 {
140 if (!myVCard->nickname().empty())
141 user->setAlias(myVCard->nickname());
142 else if (!myVCard->formattedname().empty())
143 user->setAlias(myVCard->formattedname());
144 }
145
146 const gloox::VCard::Name& name = myVCard->name();
147 user->setUserInfoString("FirstName", name.given);
148 user->setUserInfoString("LastName", name.family);
149
150 // Bug in gloox: emailAddresses should be const
151 const gloox::VCard::EmailList& emails =
152 const_cast<gloox::VCard*>(myVCard)->emailAddresses();
153 if (emails.begin() != emails.end())
154 user->setUserInfoString("Email1", emails.begin()->userid);
155
156 const gloox::VCard::Photo& photo = myVCard->photo();
157 if (!photo.binval.empty())
158 {
159 saveGroup |= User::SavePictureInfo;
160
161 // Store hash of too large picture as well to avoid downloading them again
162 if (Licq::Sha1::supported())
163 user->setPictureSha1(myPictureSha1);
164
165 if (photo.binval.size() <= 100 * 1024)
166 user->SetPicturePresent(user->writePictureData(photo.binval));
167 else
168 {
169 gLog.error("Picture for %s is too big (%zu bytes)",
170 user->id().accountId().c_str(), photo.binval.size());
171
172 user->SetPicturePresent(false);
173 user->deletePictureData();
174 }
175 }
176 else if (user->GetPicturePresent())
177 {
178 saveGroup |= User::SavePictureInfo;
179
180 user->setPictureSha1("");
181 user->SetPicturePresent(false);
182 user->deletePictureData();
183 }
184
185 user->SetEnableSave(true);
186 user->save(saveGroup);
187
188 return saveGroup;
189 }
190