/* * Copyright 2010, Alexander Botero-Lowry. All rights reserved. * Distributed under the terms of the MIT License. */ #include #include #include #include #include "AIM.h" const char* kProtocolName = "aim"; CayaProtocolMessengerInterface* gServerMsgr; AIMProtocol::AIMProtocol() { } AIMProtocol::~AIMProtocol() { Shutdown(); } status_t AIMProtocol::Init(CayaProtocolMessengerInterface* msgr) { gServerMsgr = msgr; fIMCommHandle = imcomm_create_handle(); fOnline = false; // Register callbacks imcomm_register_callback(fIMCommHandle, IMCOMM_IM_INCOMING, (void (*)())GotMessage); imcomm_register_callback(fIMCommHandle, IMCOMM_IM_SIGNON, (void (*)())BuddyOnline); imcomm_register_callback(fIMCommHandle, IMCOMM_IM_SIGNOFF, (void (*)())BuddyOffline); imcomm_register_callback(fIMCommHandle, IMCOMM_IM_BUDDYAWAY, (void (*)())BuddyAway); imcomm_register_callback(fIMCommHandle, IMCOMM_IM_BUDDYUNAWAY, (void (*)())BuddyBack); imcomm_register_callback(fIMCommHandle, IMCOMM_IM_AWAYMSG, (void (*)())BuddyAwayMsg); imcomm_register_callback(fIMCommHandle, IMCOMM_IM_IDLEINFO, (void (*)())BuddyIdle); imcomm_register_callback(fIMCommHandle, IMCOMM_IM_PROFILE, (void (*)())BuddyProfile); // TODO: Missing IMCOMM_ERROR, IMCOMM_FORMATTED_SN, IMCOMM_HANDLE_DELETED return B_OK; } status_t AIMProtocol::Shutdown() { LogOff(); return B_OK; } status_t AIMProtocol::Process(BMessage* msg) { switch (msg->what) { case IM_MESSAGE: { int32 im_what = 0; msg->FindInt32("im_what", &im_what); switch (im_what) { case IM_SET_STATUS: { int32 status = msg->FindInt32("status"); BString status_msg(""); msg->FindString("message", &status_msg); char* smsg = strdup(status_msg.String()); switch (status) { case CAYA_ONLINE: if (!fOnline) A_LogOn(); else imcomm_set_unaway(fIMCommHandle); break; case CAYA_AWAY: imcomm_set_away(fIMCommHandle, smsg); break; case CAYA_EXTENDED_AWAY: imcomm_set_away(fIMCommHandle, smsg); //UnsupportedOperation(); ? break; case CAYA_DO_NOT_DISTURB: imcomm_set_away(fIMCommHandle, smsg); //UnsupportedOperation(); ? break; case CAYA_OFFLINE: LogOff(); break; default: break; } free(smsg); BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_STATUS_SET); msg.AddString("protocol", kProtocolName); msg.AddInt32("status", status); gServerMsgr->SendMessage(&msg); break; } case IM_SET_NICKNAME: UnsupportedOperation(); break; case IM_SEND_MESSAGE: { const char* buddy = msg->FindString("id"); const char* sms = msg->FindString("message"); imcomm_im_send_message(fIMCommHandle, buddy, sms, 0); // XXX send a message to let caya know we did it BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_MESSAGE_SENT); msg.AddString("protocol", kProtocolName); msg.AddString("id", buddy); msg.AddString("message", sms); gServerMsgr->SendMessage(&msg); break; } case IM_REGISTER_CONTACTS: { const char* buddy = NULL; char *buddy_copy; for (int32 i = 0; msg->FindString("id", i, &buddy) == B_OK; i++) { buddy_copy = strdup(buddy); imcomm_im_add_buddy(fIMCommHandle, buddy_copy); free(buddy_copy); } break; } case IM_UNREGISTER_CONTACTS: { const char* buddy = NULL; for (int32 i = 0; msg->FindString("id", i, &buddy) == B_OK; i++) imcomm_im_remove_buddy(fIMCommHandle, buddy); break; } case IM_USER_STARTED_TYPING: //UnsupportedOperation(); break; case IM_USER_STOPPED_TYPING: //UnsupportedOperation(); break; case IM_GET_CONTACT_INFO: UnsupportedOperation(); break; case IM_SEND_AUTH_ACK: UnsupportedOperation(); break; case IM_SPECIAL_TO_PROTOCOL: UnsupportedOperation(); break; default: return B_ERROR; } break; } default: // We don't handle this what code return B_ERROR; } return B_OK; } void AIMProtocol::UnsupportedOperation() { } const char* AIMProtocol::GetSignature() { return kProtocolName; } const char* AIMProtocol::GetFriendlySignature() { return "AOL Instant Messenger"; } status_t AIMProtocol::UpdateSettings(BMessage& msg) { const char* username = NULL; const char* password = NULL; msg.FindString("username", &username); msg.FindString("password", &password); //msg->FindString("server", &server); //msg->FindInt32("port", &server); if ((username == NULL) || (password == NULL)) { printf("Invalid settings"); return B_ERROR; } if ((fUsername != username) || (fPassword != password)) { fUsername = username; fPassword = password; // XXX kill the handle and sign back in? } return B_OK; } uint32 AIMProtocol::GetEncoding() { return 0xffff; // No conversion, AIMProtocol handles UTF-8 ??? } status_t AIMProtocol::A_LogOn() { int ret = imcomm_im_signon(fIMCommHandle, fUsername, fPassword); if (ret == IMCOMM_RET_OK) { fIMCommThread = spawn_thread(WaitForData, "imcomm receiver", B_LOW_PRIORITY, this); resume_thread(fIMCommThread); fOnline = true; return B_OK; } return B_ERROR; } status_t AIMProtocol::LogOff() { fOnline = false; imcomm_delete_handle_now(fIMCommHandle); return B_OK; } int32 AIMProtocol::WaitForData(void* aimProtocol) { // XXX No need for aimProtocol since imcomm does its own callback thing fd_set readset; struct timeval tm; while (1) { FD_ZERO(&readset); tm.tv_sec = 2; tm.tv_usec = 500000; IMCOMM_RET ret = imcomm_select(1, &readset, NULL, NULL, &tm); if (ret != IMCOMM_RET_OK && errno == EINTR) continue; } return 0; } void AIMProtocol::GotMessage(void* imcomm, char* who, int auto, char* recvmsg) { BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_MESSAGE_RECEIVED); msg.AddString("protocol", kProtocolName); msg.AddString("id", who); msg.AddString("message", strip_html(recvmsg)); gServerMsgr->SendMessage(&msg); } void AIMProtocol::BuddyOnline(void* imcomm, char* who) { BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_STATUS_CHANGED); msg.AddString("protocol", kProtocolName); msg.AddString("id", who); msg.AddInt32("status", CAYA_ONLINE); gServerMsgr->SendMessage(&msg); } void AIMProtocol::BuddyOffline(void* imcomm, char* who) { BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_STATUS_CHANGED); msg.AddString("protocol", kProtocolName); msg.AddString("id", who); msg.AddInt32("status", CAYA_OFFLINE); gServerMsgr->SendMessage(&msg); } void AIMProtocol::BuddyAway(void* imcomm, char* who) { imcomm_request_awaymsg(imcomm, who); } void AIMProtocol::BuddyBack(void* imcomm, char* who) { BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_STATUS_CHANGED); msg.AddString("protocol", kProtocolName); msg.AddString("id", who); msg.AddInt32("status", CAYA_ONLINE); gServerMsgr->SendMessage(&msg); } void AIMProtocol::BuddyAwayMsg(void* imcomm, char* who, char* awaymsg) { BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_STATUS_CHANGED); msg.AddString("protocol", kProtocolName); msg.AddString("id", who); msg.AddInt32("status", CAYA_EXTENDED_AWAY); msg.AddString("message", strip_html(awaymsg)); gServerMsgr->SendMessage(&msg); } void AIMProtocol::BuddyIdle(void* imcomm, char* who, long idletime) { BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_STATUS_CHANGED); msg.AddString("protocol", kProtocolName); msg.AddString("id", who); msg.AddInt32("status", CAYA_ONLINE); gServerMsgr->SendMessage(&msg); } void AIMProtocol::BuddyProfile(void* handle, char* who, char* profile) { // XXX vcard is probably an issue } char * AIMProtocol::strip_html(const char* message) { char* temp; uint32 x, xnot, y, count, inhtml; size_t len = strlen(message); temp = (char*)malloc(len + 1); for (x = 0, count = 0, inhtml = 0; x < len; x++) { if (message[x] == '<' && inhtml == 0) { if (x + 10 < len) { // Convert links into [http://url] link text if (strncasecmp(message + x, "", 4) == 0) { temp[count] = '\n'; count++; x += 3; continue; } } inhtml = 1; continue; } if (inhtml) { if (message[x] == '>') inhtml = 0; continue; } if (message[x] == '&') { if (x + 4 < len) { if (strncmp(message + x, "&", 5) == 0) { temp[count] = '&'; count++; x += 4; continue; } } if (x + 5 < len) { if (strncmp(message + x, """, 6) == 0) { temp[count] = '\"'; count++; x += 5; continue; } if (strncmp(message + x, " ", 6) == 0) { temp[count] = ' '; count++; x += 5; continue; } } if (x + 3 < len) { if (strncmp(message + x, "<", 4) == 0) { temp[count] = '<'; count++; x += 3; continue; } } if (x + 3 < len) { if (strncmp(message + x, ">", 4) == 0) { temp[count] = '>'; count++; x += 3; continue; } } } if (message[x] == '\n' || message[x] == '\r') continue; else temp[count] = message[x]; count++; } temp = (char*)realloc(temp, count + 1); temp[count] = 0; return temp; }