Update list of wide characters
[centerim5.git] / src / Accounts.cpp
blobead4ef0c35105c3cc8262f45172c9a58a7006fda
1 // Copyright (C) 2007 Mark Pustjens <pustjens@dds.nl>
2 // Copyright (C) 2010-2015 Petr Pavlu <setup@dagobah.cz>
3 //
4 // This file is part of CenterIM.
5 //
6 // CenterIM is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
11 // CenterIM is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with CenterIM. If not, see <http://www.gnu.org/licenses/>.
19 #include "Accounts.h"
21 #include "Log.h"
23 #include "gettext.h"
24 #include <cstring>
25 #include <typeinfo>
27 Accounts *Accounts::my_instance_ = nullptr;
29 Accounts *Accounts::instance()
31 return my_instance_;
34 void Accounts::restoreStatuses(bool offline)
36 if (!offline) {
37 // Simply restore statuses.
38 purple_accounts_restore_current_statuses();
39 return;
42 // Set all accounts to "offline".
43 PurpleSavedStatus *saved_status;
45 // If we have used this type+message before, lookup the transient status.
46 saved_status = purple_savedstatus_find_transient_by_type_and_message(
47 PURPLE_STATUS_OFFLINE, nullptr);
49 // If this type+message is unique then create a new transient saved status.
50 if (!saved_status)
51 saved_status = purple_savedstatus_new(nullptr, PURPLE_STATUS_OFFLINE);
53 // Set the status for each account.
54 purple_savedstatus_activate(saved_status);
56 // We currently do not support saved statuses correctly so make sure this
57 // status is deleted.
58 purple_savedstatus_delete_by_status(saved_status);
61 void Accounts::openPendingRequests()
63 // Make sure there si no more than one PendingRequestWindow opened. Note that
64 // this should actually never happen because this window is opened only from
65 // the general menu and this window is a top window.
66 if (request_window_ != nullptr) {
67 // Bring the current window to the top of the window stack.
68 request_window_->show();
69 return;
72 request_window_ = new PendingRequestWindow(*this, requests_);
73 request_window_->signal_close.connect(
74 sigc::mem_fun(this, &Accounts::onPendingRequestWindowClose));
75 request_window_->show();
78 Accounts::Request::Request(PurpleAccount *account, const char *remote_user,
79 const char *id, const char *alias)
80 : account_(account)
82 remote_user_ = g_strdup(remote_user);
83 id_ = g_strdup(id);
84 alias_ = g_strdup(alias);
87 Accounts::Request::~Request()
89 g_free(remote_user_);
90 g_free(id_);
91 g_free(alias_);
94 Accounts::AddRequest::AddRequest(PurpleAccount *account,
95 const char *remote_user, const char *id, const char *alias)
96 : Request(account, remote_user, id, alias)
100 Accounts::AuthRequest::AuthRequest(PurpleAccount *account,
101 const char *remote_user, const char *id, const char *alias,
102 const char *message, bool /*on_list*/,
103 PurpleAccountRequestAuthorizationCb auth_cb,
104 PurpleAccountRequestAuthorizationCb deny_cb, void *data)
105 : Request(account, remote_user, id, alias), auth_cb_(auth_cb),
106 deny_cb_(deny_cb), data_(data)
108 message_ = g_strdup(message);
111 Accounts::AuthRequest::~AuthRequest()
113 g_free(message_);
116 Accounts::PendingRequestWindow::PendingRequestWindow(
117 Accounts &accounts, const Requests &requests)
118 : SplitDialog(0, 0, 80, 24, _("Pending requests")), accounts_(&accounts),
119 dialog_(nullptr)
121 setColorScheme(CenterIM::SCHEME_GENERALWINDOW);
123 treeview_ = new CppConsUI::TreeView(AUTOSIZE, AUTOSIZE);
124 setContainer(*treeview_);
126 for (const Request *request : requests)
127 appendRequest(*request);
129 buttons_->appendItem(
130 _("Done"), sigc::hide(sigc::mem_fun(this, &PendingRequestWindow::close)));
132 onScreenResized();
135 void Accounts::PendingRequestWindow::onScreenResized()
137 moveResizeRect(CENTERIM->getScreenArea(CenterIM::CHAT_AREA));
140 void Accounts::PendingRequestWindow::appendRequest(const Request &request)
142 const char *req;
143 if (typeid(request) == typeid(AddRequest))
144 req = _("Add");
145 else if (typeid(request) == typeid(AuthRequest))
146 req = _("Authorize");
147 else
148 g_assert_not_reached();
150 char *text;
151 if (request.alias_ != nullptr)
152 text = g_strdup_printf("[%s] %s: %s %s (%s)",
153 purple_account_get_protocol_name(request.account_),
154 purple_account_get_username(request.account_), req, request.remote_user_,
155 request.alias_);
156 else
157 text = g_strdup_printf("[%s] %s: %s %s",
158 purple_account_get_protocol_name(request.account_),
159 purple_account_get_username(request.account_), req, request.remote_user_);
161 auto b = new CppConsUI::Button(text);
162 b->signal_activate.connect(
163 sigc::bind(sigc::mem_fun(this, &PendingRequestWindow::onActivate),
164 sigc::ref(request)));
166 g_free(text);
168 CppConsUI::TreeView::NodeReference node =
169 treeview_->appendNode(treeview_->getRootNode(), *b);
170 request_nodes_[&request] = node;
173 void Accounts::PendingRequestWindow::removeRequest(const Request &request)
175 RequestNodes::iterator i = request_nodes_.find(&request);
176 g_assert(i != request_nodes_.end());
178 // If a dialog is opened for this request then close it. This happens only
179 // when the request is closed by some external event. This should never happen
180 // when the dialog/request is closed by the user.
181 if (dialog_ != nullptr && dialog_->getRequest() == &request) {
182 // It has to be an auth request.
183 g_assert(typeid(request) == typeid(AuthRequest));
185 dialog_->close();
187 // The dialog should become nullptr because the close() method triggers the
188 // OnAuthResponse() handler.
189 g_assert(dialog_ == nullptr);
192 treeview_->deleteNode(i->second, false);
193 request_nodes_.erase(i);
196 Accounts::PendingRequestWindow::RequestDialog::RequestDialog(
197 const Request &request, const char *title, const char *text)
198 : AbstractDialog(title), request_(&request)
200 addButton(YES_BUTTON_TEXT, RESPONSE_YES);
201 addSeparator();
202 addButton(NO_BUTTON_TEXT, RESPONSE_NO);
203 // Never give focus to the textview.
204 buttons_->setFocusCycle(FOCUS_CYCLE_LOCAL);
206 auto textview = new CppConsUI::TextView(AUTOSIZE, AUTOSIZE);
207 textview->append(text);
208 layout_->insertWidget(0, *textview);
211 void Accounts::PendingRequestWindow::RequestDialog::emitResponse(
212 ResponseType response)
214 signal_response(*this, response);
217 void Accounts::PendingRequestWindow::onActivate(
218 CppConsUI::Button & /*activator*/, const Request &request)
220 // We cannot have more than one request dialog opened.
221 g_assert(dialog_ == nullptr);
223 if (typeid(request) == typeid(AddRequest)) {
224 const AddRequest *add_request = dynamic_cast<const AddRequest *>(&request);
225 g_assert(add_request);
227 char *text;
228 if (request.alias_ != nullptr)
229 text = g_strdup_printf(_("Add %s (%s) to your buddy list?"),
230 request.remote_user_, request.alias_);
231 else
232 text =
233 g_strdup_printf(_("Add %s to your buddy list?"), request.remote_user_);
234 dialog_ = new RequestDialog(request, _("Add buddy"), text);
235 g_free(text);
237 dialog_->signal_response.connect(
238 sigc::mem_fun(this, &PendingRequestWindow::onAddResponse));
239 dialog_->show();
241 else if (typeid(request) == typeid(AuthRequest)) {
242 const AuthRequest *auth_request =
243 dynamic_cast<const AuthRequest *>(&request);
244 g_assert(auth_request);
246 char *text;
247 if (request.alias_ != nullptr)
248 text =
249 g_strdup_printf(_("Allow %s (%s) to add you to his or her buddy list?\n"
250 "Message: %s"),
251 request.remote_user_, request.alias_, auth_request->message_);
252 else
253 text = g_strdup_printf(_("Allow %s to add you to his or her buddy list?\n"
254 "Message: %s"),
255 request.remote_user_, auth_request->message_);
256 dialog_ = new RequestDialog(request, _("Authorize buddy"), text);
257 g_free(text);
259 dialog_->signal_response.connect(
260 sigc::mem_fun(this, &PendingRequestWindow::onAuthResponse));
261 dialog_->show();
263 else
264 g_assert_not_reached();
267 void Accounts::PendingRequestWindow::onAddResponse(
268 RequestDialog &activator, ResponseType response)
270 // Stay sane.
271 g_assert(dialog_ == &activator);
273 const AddRequest *request =
274 dynamic_cast<const AddRequest *>(activator.getRequest());
275 g_assert(request);
277 // Set early that there is no active dialog anymore. This is important because
278 // otherwise the dialog is deleted twice. Once on the Accounts::closeRequest()
279 // -> Accounts::PendingRequestWindow::removeRequest() path and once after
280 // returning from this response handling method (because dialogs are closed
281 // and deleted after a response is made by the user).
282 dialog_ = nullptr;
284 switch (response) {
285 case CppConsUI::AbstractDialog::RESPONSE_YES:
286 purple_blist_request_add_buddy(
287 request->account_, request->remote_user_, nullptr, request->alias_);
288 accounts_->closeRequest(*request);
289 break;
290 case CppConsUI::AbstractDialog::RESPONSE_NO:
291 accounts_->closeRequest(*request);
292 break;
293 default:
294 break;
298 void Accounts::PendingRequestWindow::onAuthResponse(
299 RequestDialog &activator, ResponseType response)
301 // Stay sane.
302 g_assert(dialog_ == &activator);
304 const AuthRequest *request =
305 dynamic_cast<const AuthRequest *>(activator.getRequest());
306 g_assert(request != nullptr);
308 // Set early that there is no active dialog anymore. This is important because
309 // otherwise the dialog is deleted twice. Once on the Accounts::closeRequest()
310 // -> Accounts::PendingRequestWindow::removeRequest() path and once after
311 // returning from this response handling method (because dialogs are closed
312 // and deleted after a response is made by the user).
313 dialog_ = nullptr;
315 switch (response) {
316 case CppConsUI::AbstractDialog::RESPONSE_YES:
317 request->auth_cb_(request->data_);
318 if (!purple_find_buddy(request->account_, request->remote_user_))
319 purple_blist_request_add_buddy(
320 request->account_, request->remote_user_, nullptr, request->alias_);
321 accounts_->closeRequest(*request);
322 break;
323 case CppConsUI::AbstractDialog::RESPONSE_NO:
324 request->deny_cb_(request->data_);
325 accounts_->closeRequest(*request);
326 break;
327 default:
328 break;
332 Accounts::Accounts() : request_window_(nullptr)
334 // If the statuses are not known, set them all to the default.
335 if (!purple_prefs_get_bool("/purple/savedstatus/startup_current_status"))
336 purple_savedstatus_activate(purple_savedstatus_get_startup());
338 // Set the purple account callbacks.
339 std::memset(&centerim_account_ui_ops_, 0, sizeof(centerim_account_ui_ops_));
340 centerim_account_ui_ops_.notify_added = notify_added_;
341 centerim_account_ui_ops_.status_changed = status_changed_;
342 centerim_account_ui_ops_.request_add = request_add_;
343 centerim_account_ui_ops_.request_authorize = request_authorize_;
344 centerim_account_ui_ops_.close_account_request = close_account_request_;
345 purple_accounts_set_ui_ops(&centerim_account_ui_ops_);
348 Accounts::~Accounts()
350 purple_accounts_set_ui_ops(nullptr);
353 void Accounts::init()
355 g_assert(my_instance_ == nullptr);
357 my_instance_ = new Accounts;
360 void Accounts::finalize()
362 g_assert(my_instance_ != nullptr);
364 delete my_instance_;
365 my_instance_ = nullptr;
368 void Accounts::closeRequest(const Request &request)
370 // It is not really nice to delete an object referenced by a const parameter,
371 // but well..
373 Requests::iterator i =
374 std::find(requests_.begin(), requests_.end(), &request);
375 // It is possible that the request has been already closed but it should be
376 // very very rare (and it would most likely signalize a problem in libpurple).
377 if (i == requests_.end())
378 return;
380 if (request_window_ != nullptr)
381 request_window_->removeRequest(**i);
382 delete *i;
383 requests_.erase(i);
384 signal_request_count_change(*this, requests_.size());
387 void Accounts::onPendingRequestWindowClose(CppConsUI::Window &activator)
389 // The request window is dying.
390 g_assert(request_window_ == &activator);
391 request_window_ = nullptr;
394 void Accounts::notify_added(PurpleAccount *account, const char *remote_user,
395 const char * /*id*/, const char *alias, const char *message)
397 const char *proto = purple_account_get_protocol_name(account);
398 const char *uname = purple_account_get_username(account);
400 // The code below creates four translation strings in the pot file. It is
401 // possible to reduce it only to one string but then the string contains many
402 // quirks and it is impossible to translate it without looking into the code.
403 if (message) {
404 if (alias)
405 LOG->message(_("+ [%s] %s: %s (%s) has made you his or her buddy: %s"),
406 proto, uname, remote_user, alias, message);
407 else
408 LOG->message(_("+ [%s] %s: %s has made you his or her buddy: %s"), proto,
409 uname, remote_user, message);
411 else {
412 if (alias)
413 LOG->message(_("+ [%s] %s: %s (%s) has made you his or her buddy"), proto,
414 uname, remote_user, alias);
415 else
416 LOG->message(_("+ [%s] %s: %s has made you his or her buddy"), proto,
417 uname, remote_user);
421 void Accounts::status_changed(PurpleAccount *account, PurpleStatus *status)
423 if (!purple_account_get_enabled(account, PACKAGE_NAME))
424 return;
426 LOG->message(_("+ [%s] %s: Status changed to: %s"),
427 purple_account_get_protocol_name(account),
428 purple_account_get_username(account), purple_status_get_name(status));
431 void Accounts::request_add(PurpleAccount *account, const char *remote_user,
432 const char *id, const char *alias, const char * /*message*/)
434 auto request = new AddRequest(account, remote_user, id, alias);
435 requests_.push_back(request);
436 if (request_window_ != nullptr)
437 request_window_->appendRequest(*request);
439 const char *proto = purple_account_get_protocol_name(account);
440 const char *uname = purple_account_get_username(account);
441 if (alias != nullptr)
442 LOG->message(_("+ [%s] %s: New add request from %s (%s)"), proto, uname,
443 remote_user, alias);
444 else
445 LOG->message(
446 _("+ [%s] %s: New add request from %s"), proto, uname, remote_user);
449 void *Accounts::request_authorize(PurpleAccount *account,
450 const char *remote_user, const char *id, const char *alias,
451 const char *message, gboolean on_list,
452 PurpleAccountRequestAuthorizationCb auth_cb,
453 PurpleAccountRequestAuthorizationCb deny_cb, void *user_data)
455 auto request = new AuthRequest(account, remote_user, id, alias, message,
456 on_list, auth_cb, deny_cb, user_data);
457 requests_.push_back(request);
458 signal_request_count_change(*this, requests_.size());
459 if (request_window_ != nullptr)
460 request_window_->appendRequest(*request);
462 const char *proto = purple_account_get_protocol_name(account);
463 const char *uname = purple_account_get_username(account);
464 if (alias != nullptr)
465 LOG->message(_("+ [%s] %s: New authorization request from %s (%s)"), proto,
466 uname, remote_user, alias);
467 else
468 LOG->message(_("+ [%s] %s: New authorization request from %s"), proto,
469 uname, remote_user);
471 return request;
474 void Accounts::close_account_request(void *ui_handle)
476 Requests::iterator i =
477 std::find(requests_.begin(), requests_.end(), ui_handle);
478 g_return_if_fail(i == requests_.end());
480 // This code path is only for auth requests.
481 g_assert(typeid(*i) == typeid(AuthRequest *));
483 if (request_window_ != nullptr)
484 request_window_->removeRequest(**i);
485 delete *i;
486 requests_.erase(i);
487 signal_request_count_change(*this, requests_.size());
490 // vim: set tabstop=2 shiftwidth=2 textwidth=80 expandtab: