1 // Copyright (C) 2007 Mark Pustjens <pustjens@dds.nl>
2 // Copyright (C) 2010-2015 Petr Pavlu <setup@dagobah.cz>
4 // This file is part of CenterIM.
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/>.
27 Accounts
*Accounts::my_instance_
= nullptr;
29 Accounts
*Accounts::instance()
34 void Accounts::restoreStatuses(bool offline
)
37 // Simply restore statuses.
38 purple_accounts_restore_current_statuses();
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.
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
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();
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
)
82 remote_user_
= g_strdup(remote_user
);
84 alias_
= g_strdup(alias
);
87 Accounts::Request::~Request()
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()
116 Accounts::PendingRequestWindow::PendingRequestWindow(
117 Accounts
&accounts
, const Requests
&requests
)
118 : SplitDialog(0, 0, 80, 24, _("Pending requests")), accounts_(&accounts
),
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
)));
135 void Accounts::PendingRequestWindow::onScreenResized()
137 moveResizeRect(CENTERIM
->getScreenArea(CenterIM::CHAT_AREA
));
140 void Accounts::PendingRequestWindow::appendRequest(const Request
&request
)
143 if (typeid(request
) == typeid(AddRequest
))
145 else if (typeid(request
) == typeid(AuthRequest
))
146 req
= _("Authorize");
148 g_assert_not_reached();
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_
,
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
)));
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
));
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
);
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
);
228 if (request
.alias_
!= nullptr)
229 text
= g_strdup_printf(_("Add %s (%s) to your buddy list?"),
230 request
.remote_user_
, request
.alias_
);
233 g_strdup_printf(_("Add %s to your buddy list?"), request
.remote_user_
);
234 dialog_
= new RequestDialog(request
, _("Add buddy"), text
);
237 dialog_
->signal_response
.connect(
238 sigc::mem_fun(this, &PendingRequestWindow::onAddResponse
));
241 else if (typeid(request
) == typeid(AuthRequest
)) {
242 const AuthRequest
*auth_request
=
243 dynamic_cast<const AuthRequest
*>(&request
);
244 g_assert(auth_request
);
247 if (request
.alias_
!= nullptr)
249 g_strdup_printf(_("Allow %s (%s) to add you to his or her buddy list?\n"
251 request
.remote_user_
, request
.alias_
, auth_request
->message_
);
253 text
= g_strdup_printf(_("Allow %s to add you to his or her buddy list?\n"
255 request
.remote_user_
, auth_request
->message_
);
256 dialog_
= new RequestDialog(request
, _("Authorize buddy"), text
);
259 dialog_
->signal_response
.connect(
260 sigc::mem_fun(this, &PendingRequestWindow::onAuthResponse
));
264 g_assert_not_reached();
267 void Accounts::PendingRequestWindow::onAddResponse(
268 RequestDialog
&activator
, ResponseType response
)
271 g_assert(dialog_
== &activator
);
273 const AddRequest
*request
=
274 dynamic_cast<const AddRequest
*>(activator
.getRequest());
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).
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
);
290 case CppConsUI::AbstractDialog::RESPONSE_NO
:
291 accounts_
->closeRequest(*request
);
298 void Accounts::PendingRequestWindow::onAuthResponse(
299 RequestDialog
&activator
, ResponseType response
)
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).
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
);
323 case CppConsUI::AbstractDialog::RESPONSE_NO
:
324 request
->deny_cb_(request
->data_
);
325 accounts_
->closeRequest(*request
);
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(¢erim_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(¢erim_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);
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,
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())
380 if (request_window_
!= nullptr)
381 request_window_
->removeRequest(**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.
405 LOG
->message(_("+ [%s] %s: %s (%s) has made you his or her buddy: %s"),
406 proto
, uname
, remote_user
, alias
, message
);
408 LOG
->message(_("+ [%s] %s: %s has made you his or her buddy: %s"), proto
,
409 uname
, remote_user
, message
);
413 LOG
->message(_("+ [%s] %s: %s (%s) has made you his or her buddy"), proto
,
414 uname
, remote_user
, alias
);
416 LOG
->message(_("+ [%s] %s: %s has made you his or her buddy"), proto
,
421 void Accounts::status_changed(PurpleAccount
*account
, PurpleStatus
*status
)
423 if (!purple_account_get_enabled(account
, PACKAGE_NAME
))
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
,
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
);
468 LOG
->message(_("+ [%s] %s: New authorization request from %s"), proto
,
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
);
487 signal_request_count_change(*this, requests_
.size());
490 // vim: set tabstop=2 shiftwidth=2 textwidth=80 expandtab: