diff --git a/README b/README index 8c9a75043..2d6c79031 100644 --- a/README +++ b/README @@ -1,29 +1,29 @@ Kopete, The KDE Messenger Duncan Mac-Vicar Prett , and a cast of thousands. http://kopete.kde.org ---------------------------------------------------------------------- Kopete is an Instant Messaging client designed to be modular and plugin based. It requires the KDE Desktop, version 4.7 or higher. Kopete ships with a lot of protocol plugins, supporting nine different messaging systems. All plugins have seen numerous enhancements and bugfixes since the last release and should bring your Kopete experience to a whole new level. Additionally a lot of work has gone into cleaning up the core Kopete API and GUI with several important usability-, stability- and performance-improvements. The Kopete team consists now of several active developers, working on the various plugins and the core API. However, Kopete wouldn't be in the shape in which it is now without the various contributions from KDE developers all over the world and the high-quality bug reports from active users. Thanks to all of you for your support and your interest in Kopete! As always, more bug reports and patches are welcome. -Please use http://bugs.kde.org. +Please use https://bugs.kde.org. The Kopete main developers can be contacted on our mailing list, kopete-devel@kde.org, and on IRC in #kopete, on irc.freenode.net. We always welcome new contributors. diff --git a/doc/index.docbook b/doc/index.docbook index 9d74dc55d..d1e623b9b 100644 --- a/doc/index.docbook +++ b/doc/index.docbook @@ -1,1207 +1,1207 @@ WillStephenson"> lists@stevello.free-online.co.uk"> MattRogers"> mattr@kde.org"> MichaëlLarouche"> michael.larouche@kdemail.net"> OlivierGoffart"> ogoffart@kde.org"> CharlesConnell"> charles@connells.org"> IM"> ASCII"> ]> The &kopete; Handbook &Will.Stephenson; &Will.Stephenson.mail; &Matt.Rogers; &Matt.Rogers.mail; &Michael.Larouche; &Michael.Larouche.mail; &FDLNotice; 2006-12-15 0.12 2003, 2004, 2005, 2006, 2007 &kopete; is the multi-protocol instant messenger client by the &kde; community. KDE IM Instant Messaging Jabber IRC ICQ AIM Gadu-Gadu GroupWise Novell WinPopup SMS Introduction &kopete;, the &kde; instant messaging client Before starting... If you're not familiar with Instant Messaging, please read the Getting Started section to learn about this wonderful world before continuing. What is &kopete;? &kopete; is the &kde; instant messaging (&im;) client. It allows you to communicate with your friends and colleagues using various instant messaging services. A single program is easy to learn and convenient if your friends or colleagues use more than one &im; service. &kopete; is designed to integrate well with your &kde; desktop; to make it immediately familiar. The user interface is clean and simple, without any frills to distract the user. At the same time, &kopete; aims to make communication the focus of &im;, by removing the differences between different &im; systems. One feature &kopete; introduced to support this is the Metacontact, combining the various means there are to contact someone into a single person in your contact list. Other multiprotocol instant messengers list the same person's various &im; accounts separately, making it confusing for non experienced people. &kopete; makes life easy: a metacontact is a person, and contacts are ways to communicate with that person. You will recognize contacts in a metacontact easily as small icons representing the &im; services you could use to communicate with that person. &kopete; is intended for all levels of users. Out of the box, it supports a minimal set of functions to make chatting as easy as possible. More advanced users can add extra functions such as Statistics with &kopete;'s plugin system. More &kopete; Information on the Web If you need to contact the team, the &kopete; developers' mailing list is hosted at https://mail.kde.org/mailman/listinfo/kopete-devel. If you want live support, there is an Internet Relay Chat channel for &kopete; where you can find the team discussing technical (well, not always) issues or just hanging out. You can use any IRC client to join the channel (including &kopete;), just add an IRC contact and use irc.freenode.org as the server and #kopete as the channel name. See you there! Introduction to Instant Messaging What is Instant Messaging (&im;)? &im; is a way for you to communicate instantly with your friends over the Internet. That might not sound so different to email. Have you ever noticed how cumbersome it is to have a brief conversation via email? You have to click Reply to each message, then find the right spot in the message to type something new, then send it. Then you have to wait for the next message to arrive! &im; lets you to have a conversation almost as naturally as on the phone or face to face, by typing messages into a window shared between you and your friend's screens. Another difference between &im; and email is that with &im; you can see your friends' presence, that is, whether they are actually on-line at the same time as you. This lets you send messages truly instantly, instead of sending off a mail and having to wait for your friend to check their mailbox. An &im; message pops up on the other person's screen as soon as you send it. Of course, if you'd rather not be interrupted, you can change your own presence so others will know not to disturb you. There are lots of other fun and useful &im; features you can explore with &kopete;, like group chats, file transfers and emoticons that reflect your mood. Read on to find out more! Getting Started To use &kopete; you need to set up one or more accounts for the instant messaging services you wish to use. You've probably already chosen a messaging service, either because you already use &im;, or you need to use the same service as your friends. If you don't fit into either of these categories, please consider using a messaging service based on open standards, because these are designed for use by Free Software. Other messaging services are prone to changing the underlying technology without making the details freely available, making them harder for Free Software developers to support. The messaging services that &kopete; supports that are based on open standards are Jabber and IRC. The following section assumes you are registered with an &im; service already. If not, you can register with Gadu-Gadu, and Jabber from inside &kopete;; for other services, you'll have to register using their respective web site before creating an account in &kopete;. Creating Accounts To create an account, use Settings Configure... to display the Configure window. The Configure window is the main way to set up and customize &kopete;. On the left a column of icons control which aspect of &kopete; is being configured. Click the Accounts icon. The main pane will change to display the account management pane. This is currently empty, but will soon list your &im; accounts. Click Add Account to display the Account Wizard. The Account Wizard helps you create an &im; account. On the first page, you are asked to select the messaging service that you'd like to use. Click one of the services shown and then click Next. On the following page, you should enter your registration details for that instant messaging service. Most services just require you to enter a username or unique identifying number (UIN) and password. The special purpose services Winpopup and SMS work slightly differently, so please see their specific sections. There are a couple of other options that apply to most services that you should look at: Remember passwordWhen this is checked, &kopete; will store the password for you, so you don't have to enter it every time you connect to the &im; service. If you are security-conscious or want to limit access to the &im; account you can leave this unchecked. Connect at startupWhen this is checked, &kopete; will try to connect to the &im; service as when it starts. If you use a LAN, DSL or other always-on connection, this is appropriate; dial-up modem users should turn this off and connect manually when you have dialed up. Once you've entered your &im; details, you can proceed to the Finished! page and then dismiss the wizard and the Configure window. Go Online and Start Chatting! Now you'll notice that an icon representing the account has appeared in the status bar at the bottom of the &kopete; Contact List window. This represents your current presence for this account. Right click on it and you can go online from the menu that appears. The status bar icon will animate while &kopete; connects to the &im; service. Once you're online, if you've used this &im; service before, your contacts will be fetched from the server and displayed in the Contact List. To start a chat with a contact, just click their name and a Chat window will appear. The upper part of the window is where the conversation appears - to say something, type into the bottom part of the window and click Send. If you've just created a new account you won't have any contacts. See Adding Contacts for details on how to add contacts. The shortcut for Send is set to &Ctrl;&Enter; by default; you can change it in the Chat window using SettingsConfigure Shortcuts.... Using &kopete; This chapter gives an overview of &kopete;'s basic features. We will look first at the contact list, where your contacts are displayed, and then at the Chat window, where you carry out a conversation. The Contact List The Contact List appears when you start &kopete;. It's the main window where you can set your presence, start a chat, organize your contacts, configure &kopete; and quit. Layout of the Contact List window MenuYou will usually find the menubar at the top of the contact list. If it is not there, you might have turned it off; you can re-enable it with &Ctrl;M. Details on each menu item can be found in the chapter on menu structure. Tool barThe toolbar holds the most frequently used &kopete; actions. You can customize it with Settings Configure Toolbars.... Notice the Show Offline Users and Show Empty Groups buttons. With these you can hide contacts and groups that are offline. &kopete; makes it even easier to set a status message to let your contacts know about your mood or why you're busy at the moment. Click on the Set Status Message button and start typing to enter a new message, or choose from one of the previous messages you have used. The Quick Search Toolbar quickly filters the contact list, by typing a few letters from a contact's name. Contact ListThe Contact List takes up the main part of the window. All your contacts are listed here, in the groups you have chosen for them. You can open or close groups by clicking the plus symbol adjacent to the group. You can reverse the order the groups are sorted in by clicking the Contacts label above the list. The context menu in the Contact List changes depending on the item under the mouse. Groups, Metacontacts and &im; system specific contacts have their own options. In empty areas of the Contact List, the context menu allows you to add contacts or groups, or change the viewing options for the list. Status barThe status bar shows an icon for each &im; account you have created. The icons represent the current presence of each account, which can be changed by right-clicking the account icon. &kopete; also shows your current status message in the Status Bar>. By clicking on the note icon in the corner, you can change or clear the status message as well. Setting Your Presence We introduced you to setting presence in the previous chapter. 'Presence' determines how visible you are on the &im; network. To use the network at all, you have to connect to the network, so you can send and receive messages and see others' presence. Once you are connected, most &im; systems allow you to indicate what you're doing and whether you want to chat by setting special types of presence such as Away or Free For Chat. The difference presence settings are particular to each away system; but &kopete; allows you some control all your &im; systems at once by setting them to Away or Available at the same time. You set your presence for individual &im; accounts by right clicking the account's icon in the status bar at the bottom of the Contact List. The context menu for each account lets you choose the possible presence settings for each &im; system. To change all your accounts' presence together, click the Status, or use the File Status menu. Start A Chat From The Contact List To start a chat from the Contact List, simply click a contact. A Chat window will appear. You can also right click a contact and select either Send Message or Start Chat. Send Message works differently in that it just sends a single message without opening the Chat window, using a simple dialog. Use it for fire-and-forget messages. Send A File You can send files from the Contact List, using the context menu on the person you want to send to. If &kopete; supports file transfer on their &im; system, there will be a Send File... item. Alternatively, you can drag a file from anywhere else in &kde; onto their name to start a file transfer. Organizing Contacts A Word about Metacontacts One of the principles behind &kopete; is that it offers a standardized way to use &im; systems. Differences between &im; systems are smoothed over, making it easier to communicate. We follow this principle in the way contacts are organized. When you use &kopete; you just find contacts by name; the actual &im; system used is less important. Some people have more than one &im; account - &kopete; puts the person using the account first. To support this, &kopete; introduced Metacontacts, which represent the person you want to chat with. One Metacontact contains all the different &im; IDs they may have, making it easy to see with a glance at the Metacontact 'smiley icon' whether someone is available, regardless of which &im; system they are using right now. A Word about Grouping Contacts &kopete; lets you create groups to sort your contacts. A contact may be in more than one group. Where possible, groupings are saved on server side contact lists, so if you use other &im; programs, group memberships are kept in sync. However, if you change groups in another &im; program, &kopete; cannot know to move a metacontact automatically; it is up to you to resolve this by hand. To change the group a metacontact appears in, you can use its context menu to move it or copy it to a new group, or remove it from a group. You can also use drag and drop here - just drop the metacontact on a different group name. Adding Contacts To add a contact, either select FileAdd Contact or click the Add button on the toolbar. This brings up the Add Contact Wizard. The Add Contact Wizard creates a new Metacontact using one or more &im; systems, by leading you through the following pages. Welcome Page. Here you can choose whether you want to use the &kde; Address Book for this contact. Storing &im; information in the &kde; Address Book will enable other &kde; &im; programs to share contact information with &kopete; and in future &kde; applications may use &kopete; to send information via &im;. If you prefer to keep your &im; contacts separated, clear the check box here. Choose &kde; Address Book entry. By choosing an entry from your &kde; Address Book, you can use its name as a Display Name in &kopete;. You can also create a new entry here. This page doesn't show if you chose not to use the &kde; Address Book. Select Display Name and Group. Here you can enter a Display Name (the name used for this person inside &kopete;), and choose the groups they are a member of. Select &im; Accounts. Here you can choose which accounts you want to use to chat to the new contact. If you only have one &im; account, you won't see this screen. Account-specific Add Contact Pages. For each account, you'll get one page where you can enter the UIN, buddy name or Email address, depending on the &im; system in use. Finish Screen. All done. Except if the &im; system requires authorization (such as ICQ) to add a contact to your list - in which case, you'll be prompted after the wizard exits. You can add contacts to an existing Metacontact using its context menu. Renaming Contacts You can rename a contact using EditRename Contact or with the same item on the metacontact context menu. Some &im; systems allow you to set a Display Name that is different to your username, such as Alice loves crypto!. If you change a contact's name manually, this will override their Display Name. To get it back, open the Properties dialog for that contact and check the Use the name given by the server checkbox. Removing Contacts If you no longer want a contact to be in the contact list, you can remove a Metacontact and all the contacts under it with Metacontact context menuRemove Contact. Moving Contacts between Metacontacts You can change the metacontact a contact belongs to. In practice, you only have to do this when you have just added multiple accounts to &kopete;, and you know that HotDog76 and mikejones@hotmail.com are both the same person. There are two ways to do this: Drag and DropThe contact icon to the right of the metacontact name may be dragged from one metacontact to another. Contact Context MenuThe context menu for contacts (Right-click the contact icon) allows you to choose the new metacontact from a dialog. If the move would leave a Metacontact empty (with no contacts), you'll be asked if you want to delete this contact. Removing Contacts from Metacontacts To remove a contact from a Metacontact, choose Contact context menuDelete Contact.... Configure &kopete; You can configure &kopete; using SettingsConfigure.... See the next chapter for details. Exiting &kopete; To exit &kopete; you should use FileQuit, &Ctrl;Q, or the &kopete; System Tray icon's context menu. If you just close the Contact List window, &kopete; will continue to run in the &kde; System Tray. Keyboard shortcuts The following keyboard shortcuts are supported in the Contact List window: Keyboard Shortcut Action Up Arrow Select the previous item in the contact list. Down Arrow Select the next item in the contact list. Left Arrow Close the current group. Right Arrow Open the current group. &Enter; Start a chat with the selected contact. &Ctrl;M Show/Hide the menu bar. &Ctrl;U Show/Hide offline users. &Ctrl;G Show/Hide empty groups. The Chat Window Layout of the Chat Window The Chat ViewThe Chat View usually takes up most of the Chat window and is where the conversation between you and your contacts takes place. Messages appear in the order they are received, with the earliest messages at the top of the view. You can control the appearance of the Chat View, making it look like other &im; clients or create a completely individual look. Chat Members List Since some &im; systems allow you to chat as a group, it is useful to see who is chatting at the moment. The Chat Members List appears to the left or the right of the Chat View. You can change this using SettingsChat Members List. The contact context menu is available in the Chat Members List. Input AreaThe Input Area is below the Chat View. This is where you type messages before sending them. You can change the font and color of the message using the usual tools on the toolbar. If the &im; system supports this, your messages will appear in color when your contacts read them.By default, the keyboard shortcut to send messages is &Enter;. Status BarThe Status Bar contains temporary messages, such as notification that someone else is typing, as well as the Send button. Tabbing&kopete; lets you carry on multiple conversations in one window, by putting each one in its own tab within the window. The tab titles change color to show when a new message has been received: RedSomeone typed a message. GreenSomeone is typing a message. BlueSomeone typed a message containing your nickname. There are several different ways to control grouping. To configure this behavior, go to the Chat tab of the Behavior page of the Configure &kopete; dialog. You can also move chats between windows using the Tabs menu, and control the placement of the tabs in the window. Group Chats in &kopete; You can use &kopete; to chat one to one, or in a group, where the &im; system supports this. To invite others into a chat, drag them from the Contact List into the chat window, or use ChatInvite<contact name>. File Transfers Some &im; systems allow you to send and receive files. You can access this function from the contact's context menu. If you're already chatting, and want to send a file, simply drag the file from any other part of &kde; into the Chat Window, or select the ChatSend File menu. Keyboard Shortcuts The following keyboard shortcuts are supported in the Chat window: Keyboard Shortcut Action &Enter; Send the message in the Input Area. &Ctrl;P Print the contents of the Chat View. &Ctrl;S Save the contents of the Chat View. &Ctrl;W Close the current Chat View. The Chat window will close unless there is more than one tab in the window. &Ctrl;, Change to the previous tab. &Ctrl;. Change to the next tab. &Ctrl;&Shift;B Detach a tabbed chat into a separate window. Complete a partially typed nickname belonging to someone you're chatting with. Configuring &kopete; To configure &kopete;, look in the Settings menu. Global Shortcuts &kopete; defines some shortcuts which are valid in any &kde; application. Global Keyboard Shortcuts Action &Ctrl;&Shift;I Read Message. This is useful if you have hidden the Contact List window and the system tray icon is animating to tell you you have a new message. &Ctrl;&Shift;C Show/hide (Dock) the Contact List window. Warning: If you have disabled &kopete;'s System Tray icon or don't have a system tray, this can make the Contact List vanish - the only way to restore it is to repeat this shortcut. The Configure &kopete; Dialog Adding and Editing Accounts We briefly showed you how to add an account in Getting Started. To change an account's settings later, open up the Configure &kopete; dialog, with SettingsConfigure.... Much like the &kde; &systemsettings;, the configuration is separated into sections. The icons on the left side of the dialog switch between sections. On the Accounts page, you can Add, Remove, or Modify accounts. Editing accounts is much the same as adding them, but note that you cannot change the UIN, buddy name, or whatever account identifier your &im; system uses. This is intrinsic to the account. If you want to change this, you will have to add another account with the new account identifier and (optionally) remove the old account. You can quickly distinguish between multiple accounts using the same &im; system by giving a custom color to each account's status bar entry and contact icons. To do so, select the account and click the color selector on the right side of the page. You can control the priority of accounts using the Up and Down icons on this page. If you have more than one way to message a contact, this determines the &im; system &kopete; will use to communicate them. Global Identity &kopete;'s Global Identity lets you set your own nickname and photo once for all your &im; accounts. You can read these details from the &kde; address book entry for yourself, from a single one of your contacts, or add a completely new nickname or photo. If you have an exciting dual life, you can create multiple identities and switch between them in the Identity section. Behavior Behavior covers the way &kopete; integrates with your desktop, Away settings, and chat user interface options. The General tab Here you can customize &kopete;'s desktop integration, and control the way the contact list is laid out. Show system tray icon By default, &kopete; places an icon in the &kde; System Tray. If you prefer, you can turn this off here. Start with hidden main window This causes &kopete; to start with the Contact List window hidden (docked). You can make it visible by clicking the system tray icon or with the Show Contact List global shortcut. Open messages instantly New messages open chat windows as soon as they arrive. Use message queue The message queue is where &kopete; puts messages when there is no chat window open. This allows you to be notified of new messages with popup speech bubbles; or by animating the System Tray icon. If you choose to disable the message queue, chat windows will open as soon as you receive a new message. Use message stack If you use a message stack, &kopete; shows recently received messages starting with the last message received. Notifications Show a bubble on new message This option shows a speech bubble; coming from the System Tray icon when you receive a new message. You can start a chat or ignore the message. &URL;s are extracted from the message; if you click a link, your preferred browser will open the link and the message will be dismissed. Flash the system tray on new messages This option causes the System Tray icon to animate when you receive a new message. Clicking the icon will show the message in a chat window. Enable events while away If you do not wish to be distracted by these notifications while you are set Away, uncheck this box. Configure Sounds & Events Sounds, flashing taskbar entries, passive popups and more exotic notifications are supported in &kopete; using the &kde; notification system. Type help:/kcontrol/kcmnotify in &konqueror; or select the Help tab in the System Notifications section of the &systemsettings; for more information. To add custom notifications for a contact, right click that contact in the Contact List and select Properties. This lets you start chats, play a custom sound effect, or display a message for that contact or group. Otherwise you can use the Execute a program notification to perform custom notifications. As an example, if you have XOSD (X On-Screen Display) installed, you can get OSD online notifications by executing the following command when the User goes online event takes place: echo %s | osd_cat -o 100 -p bottom -A center -f -*-helvetica-*-r-*-*-24-*-*-*-*-*-*-* -O 2 -c gold OhReally at the &kde; Forum suggests having your online notifications read out by a speech synthesizer, using MBROLA like so: echo %s | sed -e 's/online/onlaain/i' | /usr/local/bin/mbrdico.dutch.female The 'sed' in the middle phoneticises &kopete;'s output to so the synthesizer has a better Dutch pronunciation. Away Settings Notify all open chats when I go away Be careful if you enable this item; it will cause a message to be sent to open chats when you become away, which some people may find intrusive. Auto Away Here you can choose to have &kopete; set you away after a period of inactivity. Predefined Away Messages You can define as many custom away messages as you like here, and choose from them when you go Away using the Status button on the main toolbar. Chat Settings Raise window/tab on new messages This causes a chat window to pop up when it receives a new message. Show events in chat window Some &im; systems can give extra information, like people joining or leaving chats. This option lets you receive these messages in your chat window. Highlight messages containing your nickname This simply emphasizes messages in a chat that contain your nickname. For more powerful control over highlighting and other events, see the Highlight plugin. Interface Preference &kopete; can send messages using either a fire and forget interface that does not wait for a reply, or a chat window where the conversation is visible as it unfolds. Here you can choose which style to use by default. Chat Window Grouping Policy If you wish to group chats within tabs in a single window, &kopete; lets you choose several ways to determine the grouping. Open all messages in a new chat window Group all messages from the same account in the same chat window Group all messages in the same chat window Group all messages in the same group in the same chat window Group all messages from the same metacontact in the same chat window Chat Window Line Limit Maximum number of chat window lines This limits the number of lines of text the chat window can display. Appearance Appearance governs the style of the Chat window, its colors and fonts, and lets you choose your preferred emoticons. Emoticons Emoticons (also known as smileys) are combinations of characters such as ;-)that look like a face, and communicate moods or expressions. &kopete; can optionally use graphical emoticons in place of the characters themselves. On this tab, you can select which emoticon set you prefer, or turn off graphical emoticons altogether. Chat Window Styles The style of the chat view can be altered to look like other clients. Installed styles are shown in the list on the left and are previewed in the main panel. See Chat Window Style guide for a document how to make your own style. Third party styles are available at the KDE Store. &kopete; 0.12 now supports styles from Adium(an &im; program on &Mac; OS X). So you can download styles from Adium here: Adium Xtras and select Message View Styles. To install a style, click Install.... Select an archive file containing the style. To delete a style, select a style in the list and click Delete. Group consecutive messages is a useful option to make your chats more readable. If you receive several messages in a row from the same contact, they are grouped without repeating the sender name. Contact List Arrange metacontacts by group By disabling this, &kopete;'s groups are hidden, and contacts are divided only into Online Contacts and Offline Contacts. Show tree branch lines Usually &kopete; displays contacts and groups as a tree, where group members are indented. For a simpler appearance, you can disable this, so the contact list becomes a flat list. You can also control whether branches are indented here. Contact Display Mode There are several ways you can present the contact list here. Of particular interest may be the Use contact photos when available option, that shows the contact list using photos chosen by your contacts or the &kde; Address Book Contact List Animations This controls the degree of animation of the contact list. Turning this off will make &kopete; more responsive on slower machines. Contact List Auto-Hide By enabling this, the contact list will automatically disappear a few seconds after the pointer leaves the window. Change Tooltip Contents... You have a lot of control over how much or how little detail appears in the tooltips shown on the contact list using this dialog. Colors and Fonts Chat Window Colors Here you can alter the base font and text colors used for chatting. Formatting Overrides If your contacts tend to choose fonts and colors that you dislike, you can tell &kopete; to ignore these and use your regular font. Contact List Some &im; systems let you see whether contacts are idle at their computers. This option enables you to change the color used for idle contacts. Devices The Devices section allows you to choose and configure which multimedia devices are used for A/V chatting. Whether this works for you is highly dependent on the hardware you have and how well it is supported by your operating system. Video &kopete; uses the Video4Linux 2 system for video. This shows a blue square if no video device is found, or a preview if the camera is working. For up-to-date information on &kopete; webcam support, see the Kopete Webcam Support wiki page. Audio Audio support in &kopete; is at an experimental stage. If you have an Audio tab, you are probably using a preview build of &kopete;. Loading Plugins You can customize &kopete; with special functions that may be useful or just a bit of fun. Bring up the Select and Configure Plugins panel with SettingsConfigure... and then the Plugins entry. Plugins can be turned on or off in the list on the left side of the page. Each plugin may be configured on the right side. See the chapter on plugins for details on each plugin. &kopete;'s Protocols &kopete; calls different &im; systems 'Protocols'. When you add an account, it is specific to a single protocol. Although &kopete; tries to make instant messaging appear the same, no matter what protocol you use, there are some differences in the level of support for advanced features such as file transfer and multimedia. Protocols AIM AIM supports chatrooms. Use the Join Chat... command on the AIM account menu to join a chatroom. Contact pictures and custom emoticons are also supported. ICQ ICQ has an Invisibility feature which allows you to hide from selected contacts. You may also search the ICQ user folder when adding a contact. A wide range of contact details can be set using the Properties option. Jabber Jabber, also known as XMPP, supports file transfer, conferencing and any other services supplied by the Jabber server. For example, many Jabber servers have a user directory, and some provide transports to other messaging systems. To access services, use Services... on the account menu. Jabber file transfer can work without port forwarding, but enjoys better performance where a direct connection is possible. By default, port 8010 is used for port forwarding, but this is configurable in each account's settings. Google Talk Since Google Talk is based upon Jabber, it is well supported in &kopete; with the exception of voice chat, which is worked upon. To configure &kopete; for Google Talk: Use your complete Google Mail address as the user name. Check Use protocol encryption (SSL), Allow plain-text password authentication and Override default server information. The server is talk.google.com or gmail.com and ports 443 or 5223 should be used. Novell GroupWise GroupWise Messenger is an enterprise messenging system from Novell Inc. The full range of features are supported, including privacy, group chat, rich text and user search. Gadu-Gadu Gadu-Gadu is a chat system originating from Poland. At present, &kopete; supports basic chat functions. WinPopup WinPopup is a way to use &kopete; to send and receive messages with &Windows; computers on the local network. The WinPopup protocol only supports single, plain-text messages. Other protocols As well as the protocols named above, &kopete; has support for several other protocols. In most cases, this is not enabled by default or an additional plugin must be installed. Meanwhile, SMS, Skype and SILC are provided in this way. However, the &kopete; team are not responsible for these protocols. &kopete;'s Plugins &kopete; offers plugins that provide functions that aren't essential for messaging, but are useful for some people. Plugins Auto Replace Auto Replace allows you to correct frequently misspelled words or save typing certain words using abbreviations. Bookmarks The Bookmarks plugin creates bookmarks in your &kde; bookmarks list from URLs that are received in &im; messages. Contact Notes Contact Notes allows you to note down any useful bits of information on a metacontact. &Olivier.Goffart; &Olivier.Goffart.mail; &Charles.Connell; &Charles.Connell.mail; 2007-10-14 1.3.0 Cryptography The Cryptography plugin allows you to send and receive encrypted and/or signed messages. Cryptography lets you use an OpenPGP implementation to encrypt and sign messages in conversations. Note that this is not the same as an SSL secured chat session. Cryptography can encrypt your messages to a single individual so that only the holder of the matching key can read it. If you wish that the recipient of your messages can verify that you have sent your messages, you may sign messages. Signing and encryption can be used separately or together. When you receive a message, if it has been encrypted, you will see a lock icon. If it has been signed with a verifiable signature (you have the public key of the sender), you will see a pen icon. For a non-verifiable signature, a red pen icon will be shown. To configure Cryptography, select your private key in the plugin configuration page. Then, using Select Public Key... from each meta-contact's context menu, choose their public key. You will be prompted for your passphrase when using this plugin. Due to limitations in some of the instant messaging protocols that &kopete; uses, encrypted messages cannot be sent with those protocols. If you attempt to use an unsupported protocol, you will be warned. Cryptography can use the public keys stored in your &kde; address book for each contact. However, it must be able to know which address book entry is associated with which meta-contact. To provide this information, choose Properties from the meta-contact context menu, and then set the Address Book Link. Cryptography can also export public keys that you have set to your address book. If a meta-contact has an address book link, then the key will go in the correct address book entry, replacing an existing one. If no link exists (possibly because no address book entry exists for the individual in question), a new entry will be created. This entry will have two fields filled: the public key from &kopete; and the meta-contact's display name. Documentation copyright © &Olivier.Goffart; &Olivier.Goffart.mail; and © 2007 &Charles.Connell; &Charles.Connell.mail; Highlight Highlight works a little like email filters, in that it allows you to make things happen in response to particular messages. As well as highlighting the text, you can play sounds. History The History plugin, when activated, records conversations using any &im; system and allows you to view old conversations later. A History item appears in each Metacontact's context menu so you can view the message history for that metacontact. The following item is added to the Contact List's menus: Edit View History (Enabled when a contact is selected) This displays the History browser for the selected contact. The following items are added to the Chat window's menus: &Alt;&Shift;Left Arrow Tools History Previous This enables you to view the next oldest set of messages from the History in the Chat window. &Alt;&Shift;Right Arrow Tools History Next This shows the next newest set of messages from the History in the Chat window. Tools History Last This shows the most recent set of messages from the History in the Chat window. KopeteTeX KopeteTeX allows scientists and mathematicians to hold conversations using the LaTeX markup language. Expressions entered within $$ are rendered as a graphic in the chatwindow, and can be cut and pasted as the original Latex. To use this plugin you must have LaTeX installed Now Listening With the Now Listening plugin, let people you're chatting with know what you're listening to, by typing /media in a chat, or with ToolsSend Media Info in the Chat window. OTR The OTR plugin allows you to encrypt conversations using the Off-The-Record encryption method. This encryption method basically works like the PGP encryption but it is optimized for instant messaging and handles the key exchange on its own. The only thing you have to do, is to make sure that you really are writing to the person you expect by using one of OTR's authentication mechanisms. Menu items The following item is added to the Contact List's menus: Edit OTR Policy (Enabled when a contact is selected) You can set the encryption policy for each contact here. Setting this entry to "Default" causes &kopete; to use the default setting in the configure dialog The following items are added to the Chat window's menus: Tools OTR Settings Start OTR session This lets you initiate an OTR encrypted session. Tools OTR Settings End OTR session This lets you end a previously established OTR session. Tools OTR Settings Authenticate contact Use this menu entry if you wish to authenticate the contact. The settings dialog In the setting dialog you have various options to set the modules behaviour during your chatsessions. The most important of them is the default policy configuration. Set the policy to "Opportunistic" if you wish &kopete; to check if you contacts support OTR and establish an encrypted session on its own or to "Manual" if you wish to control each encryption state on your own. The options "Always" and "Never" are recommended only for experienced users that wish to set the policy on a per-contact base. In the "Private Keys" Pane you have the possibility to create you personal keypair for each account. You don't need to do this manually. If an account does not have a keypair it will be generated automatically the first time it is required. In the "Known Fingerprints" tab you have the possibility to see, verify and delete known public fingerprints of your contacts. Authentication You've probably received email from people pretending to be banks, credit agencies, even wealthy Nigerian expatriates. People lie about who they are all the time on the Internet. Authentication is a way to make sure that nobody can lie to you about who they are when they use OTR. When to authenticate You should authenticate a buddy the very first time that you talk to them using OTR. If you don't, then you cannot really be sure that someone else isn't impersonating them or trying to listen in on your conversation. However, once you've authenticated your buddy once, you don't have to do it again. OTR will automatically do the authentication for all of your future conversations with that buddy. The only exceptions occur when your buddy switches between multiple computers or multiple &im; accounts. In this case, you will need to authenticate once for each computer and account. Once you've done this, your buddy can freely use any of the computers you've authenticated them on, and OTR will recognize them automatically. If your buddy uses a new computer or account that OTR does not recognize, a message will pop up in your conversation window telling you about it. When to authenticate How to authenticate To authenticate someone, open a conversation with them and click on "Authenticate Contact" on the OTR button. A wizard will be shown, asking you what authentication method you would like to use. Please follow the steps in this wizard. If the authentication was successful, then you know that you are really talking to your friend. Any imposter should have a hard time guessing what you're typing in, so you'll be able to catch them in the act. Pipes Pipes allows you to pipe incoming or outgoing messages through an external script or executable. An incoming message will be piped through the executable before it reaches your chat window, while outgoing messages will be piped through the executable before they are sent over the Internet. Pipes can receive text in one of three forms: Plain text body The body of the message, in plain &ascii; text &HTML; text body The body of the message, in &HTML;, as it would be if your chat peer had sent &HTML; to you, which will be rendered in the chat window &XML; full message An &XML; document that describes all the characteristics of the message, including the &HTML; body. For most purposes, you probably want to use the plain text body or &HTML; text body options. These can be used with programs such as translators or summarizers. The &XML; format is only appropriate for use with a program or script written specifically to work with this plugin. If you would like to write a program or script that does work with this plugin, see the more detailed documentation. Statistics This plugin uses a database to gather information about your contacts' activity patterns. You can use this to see when a contact is usually online, for example. Text Effect Text Effect applies funny effects to your messages before sending them, like coloring them or changing the case of the words. Just don't forget you have activated it - we've had bug reports from forgetful Text Effect users! Translator The Translator plugin lets you define a preferred language for each Metacontact, and then translates messages to or from them using web based translation services such as Google. Set your own preferred language in the Select and Configure Plugins dialog. Each contact's preferred language can be set on its context menu. The following item is added to the Chat window's menus: &Ctrl;T Tools Translate If you did not turn on automatic translation, this translates the current chat. Web Presence Web Presence allows you to publicize your &im; presence on the Web. Give it the path to a file on an &FTP; server (for example), and it will upload a short piece of &HTML; to that file, which you can include in your homepage. &kde;'s network transparency makes this simple. Useful for bloggers to make friends with, or you could use it to use &im; in your business. Example: sftp://username@somehost.org/path/to/homes/user/im.html uses the SFTP protocol to upload your presence directly onto the webserver. See the KIO manuals for tips on specific network protocols. Contributing a plugin &kopete; is designed to make it easy to create plugins that give it extra functions. So if you've got a great idea to make &kopete; even better, get in touch! &kopete-menus; Frequently Asked Questions What does &kopete; mean? How do I pronounce it? &kopete;'s name comes from the chilean word Copete, meaning a drink with your friends. Duncan, the original author, recorded an audio sample. When I have more than one messaging service under a user's name in my contact list and I click on that user's name, it will message them on the wrong messaging service. You can change the order of accounts &kopete; tries to message people with by using the Up and Down arrows in the bottom right corner of the account configuration screen. &kopete; will try to connect with accounts starting from the top. However, if one service has a higher status value than the others for that user, &kopete; will use that one. For example, if a person has three services and two are marked as away and the third is marked as online, &kopete; will always try to message the user using the online service.If you click on the small protocol icon on the right of the menu item, instead of on the person's name, you will always try to contact the person using that service! I need to connect via a SOCKS proxy, but I cannot find any proxy configuration options in &kopete;. How do I set up &kopete; to use SOCKS? ICQ, AIM, and Jabber use the &kde; network infrastructure. Their SOCKS proxy details are configured with the rest of &kde;, in &systemsettings;, Network and ConnectivityNetwork SettingsProxy. Is it possible to customize the icons I see in &kopete;? You can switch between different emoticons using the Emoticons tab of the Appearance page of the Configure &kopete; dialog. To install new emoticons, first look at the KDE Store, where there are a lot of additional emoticon sets to download. The emoticons are easy to install - you just place a folder containing the icon files along with an &XML; file describing the mapping from text to picture in $KDEDIR/share/apps/kopete/pics/emoticons (or $KDEHOME, for example, in /home/joeuser/.kde/). Copy the extracted folder to $KDEDIR/share/apps/kopete/pics/emoticons or $HOME/.kde/share/apps/kopete/pics/emoticons (or wherever $KDEHOME is) Select Configure &kopete; from the Settings menu and click on Appearance in the left panel of the Preferences window and click on the Emoticons tab Select the emoticons set you just installed from the list Now you can use the newly installed emoticons in &kopete; To replace the protocol icons, you'll have to replace the icons in $KDEDIR/share/apps/kopete/icons, or provide replacements to override them in the same folder under $KDEHOME. At present there aren't any complete replacement sets that you can simply extract there. I don't see any plugins listed in the Configure Plugins dialog. What's up? There are two solutions to this problem. If you used binary packages, then make sure that you used binary packages designed for your distribution, and the specific version of your distribution. Crossing distributions and versions around will not work with &kopete;. If there are no binary packages for your system, you must compile &kopete; using the source packages. If you compiled your &kde;, then &kopete; must go in the same prefix as &kde; did. If, however, you compiled the source packages, then make sure that your &kopete; is installed in the same location as your &kde;, if it is not, you can force it to do so by specifying a prefix command to cmake. Example: cmake -DCMAKE_PREFIX=/opt/kde4 (where /opt/kde4 is your prefix). To determine where your &kde; prefix is, type which dolphin in a &konsole; window. It should display something like /opt/kde4/bin/dolphin, then /opt/kde4 would be your prefix, or if it says /usr/bin/dolphin, then your prefix would be /usr. Also make sure you performed a make install, and check that there is no stale &kopete; installation from previous versions in different locations. You might also need to run kbuildsycoca4 manually after installing &kopete;. kbuildsycoca4 updates the plugin database. It runs when new software using plugins is installed, but it is possible that kopete will run &kopete; before the update has completed. I have many <desktops / monitors / computers> and / or I talk to many people at the same time, and as a result I wish &kopete; could play a <sound / pop up a message / change my wallpaper / reboot my computer > whenever I <receive a new message / someone goes online / someone goes offline > When will this be added? It is already there (surprise!). &kopete; is a &kde; application and thus makes use of KNotify for all notifications. For those who don't know, KNotify is a very powerful architecture that lets you do pretty much anything you want when an event occurs in a &kde; application. You can play a sound, pop up a passive dialog, log a message, or even run your own custom commands, such as a shell script, to do anything your heart desires. To access this treasure trove, go into the SettingsConfigure Notifications dialog. This will present you with all the different actions you can take on the events listed just above. As a tip, check out some of your other &kde; applications to see what events they let you catch. I for one find a passive popup on new emails in &kmail; to be very handy. How come file transfers don't work? There are many reasons for this. WLM was the first protocol in which we've implemented support for file transfers. Support for file transfers in Jabber and IRC was added in &kopete; 0.9.0 (included in &kde; 3.3), and ICQ and AIM in &kopete; 0.50.0 (included in &kde; 4.0.0). Also, file transfers may not work if you're behind a firewall - see WLM section in the protocol chapter. Why all of this silly single-click behavior? It's very annoying! Kopete is a &kde; application. We are not some lame 3rd party application that decides to "look" like the rest of KDE. We also have the "feel" and "integration" aspect of KDE, which means that &kopete; follows KDE's standard "Single-click" behavior. If you would like to change this it requires you to change it in all of KDE. You can change to a double-click style by starting &systemsettings; and go to Input Devices Mouse and change to the option Double-click to open files and folders (select icons on first click). Will you make the user-interface skinnable? The only answer for this is no. &kopete; was invented to be a &kde; Instant-Messenger and this also includes having a &kde; Look-n-Feel. By adding skins we would break this rule and having both skin-support and skinless-support at once is impractical as well. Just select a different style in &kde;'s &systemsettings; and be happy to have transparent menus and other neat eye-candy today's &kde; offers. You can, however, run kopete using a different style. Just run kopete --style=marble as an example. Any valid &kde; or &Qt; style will work. I would like a feature in the next version of Kopete. -Please go to &kde; Bugzilla and file a wishlist item under the application 'kopete'. Please be very detailed and proof-read what you have written to us. This is NOT a guarantee that we will add this feature in any version, but allows us to consider it. +Please go to &kde; Bugzilla and file a wishlist item under the application 'kopete'. Please be very detailed and proof-read what you have written to us. This is NOT a guarantee that we will add this feature in any version, but allows us to consider it. My &kopete; crashed or has/had unexpected behavior (unexpected behavior does NOT fall in the category of "Kopete doesn't act like Pidgin", &etc;). -Please report this bug to us by going to &kde; Bugzilla and file a bug report under the application 'kopete'. Bugs will only be accepted if they are in English, they are detailed, and if a crash was involved they contain a backtrace (the text in the tab marked Backtrace once &kopete; has crashed). If you are using &kopete; git, however, please mail us at &kopete; developers mailing list instead of filing a bug report. +Please report this bug to us by going to &kde; Bugzilla and file a bug report under the application 'kopete'. Bugs will only be accepted if they are in English, they are detailed, and if a crash was involved they contain a backtrace (the text in the tab marked Backtrace once &kopete; has crashed). If you are using &kopete; git, however, please mail us at &kopete; developers mailing list instead of filing a bug report. Jabber SSL support stopped working, I get the message that the QCA TLS plugin is probably missing. What am I supposed to do? As the error message indicates, you are missing the QCA TLS plugin, which handles the Jabber plugin's TLS/SSL encryption. Install the package qca-tls (and eventually qca-ossl) or retrieve the plugin from its homepage. Please note that you only need to install the qca-tls plugin, not the QCA library itself. A recompilation of &kopete; is not necessary, in fact not even a restart. In case you have installed the plugin and it still won't work, Qt can probably not find it. Since qca-tls installs itself as a &Qt; plugin, you will have to make sure that it gets installed to the plugin directory of the &Qt; version you are using to run &kopete;. I use Jabber and ICQ together. Whenever I go online with Jabber, I am disconnected from ICQ with an error message about being connected from another client. What's up? Remember when you played with ICQ transports on that other Jabber client? You set up an ICQ Transport which allows you to talk to ICQ people from Jabber. ICQ doesn't allow multiple connections to the same account, and disconnects you. Look in your Jabber contacts for a contact of the form 123456@icq.jabber.org and delete it. Credits and License &kopete;: copyright 2001-2007, &kopete; Developers &underFDL; &underGPL; The Team Current Development Team Pali Rohár (pali.rohar at gmail com): Developer and Maintainer Duncan Mac-Vicar Prett (duncan at kde org): Original author, developer, and project leader Till Gerken (till at tantalo net): Developer, Jabber maintainer Olivier Goffart (ogoffart at tiscalinet be): Lead Developer, WLM Plugin Maintainer Andy Goossens (andygoossens at telenet be): Developer Grzegorz Jaskiewicz (gregj at pointblue com pl): Developer, Gadu-gadu Plugin Maintainer Jason Keirstead (jason at keirstead org): Developer &Matt.Rogers; (mattr at kde org): Lead Developer, AIM and ICQ plugin maintainer Richard Smith (lilachaze at hotmail com): Developer, UI maintainer &Will.Stephenson; (lists at stevello free-online co uk): Developer, icons, plugins, manual author Michel Hermier (michel.hermier at wanadoo fr): IRC Plugin Maintainer Andre Duffeck (andre at duffeck de): Developer: Developer, Yahoo plugin maintainer &Michael.Larouche; (michael.larouche at kdemail net): Developer, WLM, Chat Window. Former Developers (&kopete; Hall Of Fame) These people have moved on from &kopete;, so don't contact them for &kopete; support. We're eternally grateful for their contributions. Christopher TenHarmsel (tenharmsel at users sourceforge net)Developer, Oscar hacker Ryan Cumming (ryan at kde org): Core developer Richard Stellingwerff (remenic at linuxfromscratch org): Developer Hendrik vom Lehn (hennevl at hennevl de): Developer Stefan Gehn (sgehn at gmx net): Developer &Robert.Gogolok; (robertgogolock at gmx de): Developer &Nick.Betcher; (nbetcher at kde org): Original author of ICQ, AIM and IRC plugins Daniel Stone (dstone at kde org): Original Jabber plugin author James Grant (topace at lightbox org): Developer, importer Plugin author Zack Rusin (zack at kde org): Developer, old Gadu-gadu Plugin author Gav Wood (gav at kde org): WinPopup Plugin author Martijn Klingens (klingens at kde org): Developer, WLM hacker Documentation Documentation copyright 2003,2004,2005 &Will.Stephenson; (lists at stevello free-online co uk), copyright 2005 &Matt.Rogers; (mattr at kde org), copyright 2005,2006 &Michael.Larouche; (michael.larouche at kdemail net). &kopete-chatstyle; &kopete-pipes; &kopete-jabber; &kopete-icq; &kopete-emoticonspec; &documentation.index; diff --git a/protocols/gadu/README.gadu b/protocols/gadu/README.gadu index 6c7a929e2..b84280d78 100644 --- a/protocols/gadu/README.gadu +++ b/protocols/gadu/README.gadu @@ -1,43 +1,43 @@ GaduGadu is primarily used and created for Poles, but it does not mean that it cannot be used by anyone else. There is only one small issue, by design protocol uses cp1250 encoding. Unfortunately, there's nothing we can do about that, as it's needed to maintain compatibility with the Windows client, which is the only one supported (and created) by the creators of gadu-gadu - sms- express company. Gadu-Gadu plugin uses libgadu. If it's not avaliable in the system, it will automaticaly switch to it's copy in /protocols/gadu/lib (v1.5). Following describes what to do if you still want to use external library version: You can download libgadu (part of ekg package) from http://dev.null.pl/ekg, This should be version 1.5 (release candidate or stable), please stick with version 1.5 if possible. Author of libgadu/ekg does not keep any compatibility between minor releases. It should support protocol version 6.0 by default. You have to download the ekg communicator package (which is a gadugadu client for the console). To install from sources, run: ./configure --enable-shared --disable-static --enable-dynamic --with-pthread --prefix=/usr make (and as root) make install. It is also available pre-packaged, e.g. for debian unstable, by default as libgadu2 (binary) and libgadu-dev (development). In order to compile kopete from sources, including the gadu-gadu plugin you will need the libgadu headers and libraries installed and set up in your system. Please refer INSTALL for information about building kopete from sources. If you're looking for more information, please refer to http://kopete.kde.org and read the FAQ . If you have found an error in kopete, or in the gadu-gadu plugin -- please use the kde bug tracking system at http://bugs.kde.org/ +- please use the kde bug tracking system at https://bugs.kde.org/ Grzegorz Jaskiewicz aka Kain/K4 gj AT pointblue DOT com DOT pl diff --git a/protocols/groupwise/libgroupwise/gwclientstream.h b/protocols/groupwise/libgroupwise/gwclientstream.h index 0908f877a..44b098d57 100644 --- a/protocols/groupwise/libgroupwise/gwclientstream.h +++ b/protocols/groupwise/libgroupwise/gwclientstream.h @@ -1,187 +1,187 @@ /* gwclientstream.h - Kopete Groupwise Protocol Copyright (c) 2004 SUSE Linux AG http://www.suse.com Based on Iris, Copyright (C) 2003 Justin Karneges Kopete (c) 2002-2004 by the Kopete developers ************************************************************************* * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * ************************************************************************* */ #ifndef GW_CLIENTSTREAM_H #define GW_CLIENTSTREAM_H #include #include #include "gwfield.h" #include "libgroupwise_export.h" #include "stream.h" // forward defines class Connector; class Request; class TLSHandler; struct NovellDN { QString dn; QString server; }; class LIBGROUPWISE_EXPORT ClientStream : public Stream { Q_OBJECT public: enum Error { ErrConnection = ErrCustom, // Connection error, ask Connector-subclass what's up ErrNeg, // Negotiation error, see condition ErrTLS, // TLS error, see condition ErrAuth, // Auth error, see condition ErrSecurityLayer, // broken SASL security layer ErrBind // Resource binding error }; enum Warning { /*# WarnOldVersion, // server uses older XMPP/Jabber "0.9" protocol // can be customized for novell versions*/ WarnNoTLS // there is no chance for TLS at this point }; enum NegCond { HostGone, // host no longer hosted HostUnknown, // unknown host RemoteConnectionFailed, // unable to connect to a required remote resource SeeOtherHost, // a 'redirect', see errorText() for other host UnsupportedVersion // unsupported XMPP version }; enum TLSCond { TLSStart, // server rejected STARTTLS TLSFail // TLS failed, ask TLSHandler-subclass what's up }; enum SecurityLayer { LayerTLS, LayerSASL }; enum AuthCond { GenericAuthError, // all-purpose "can't login" error NoMech, // No appropriate auth mech available BadProto, // Bad SASL auth protocol BadServ, // Server failed mutual auth EncryptionRequired, // can't use mech without TLS /*# InvalidAuthzid, // bad input JID // need to change this to novell DN*/ InvalidMech, // bad mechanism InvalidRealm, // bad realm MechTooWeak, // can't use mech with this authzid NotAuthorized, // bad user, bad password, bad creditials TemporaryAuthFailure // please try again later! }; enum BindCond { BindNotAllowed, // not allowed to bind a resource BindConflict // resource in-use }; explicit ClientStream(Connector *conn, TLSHandler *tlsHandler = 0, QObject *parent = nullptr); ~ClientStream(); void connectToServer(const NovellDN &id, bool auth = true); void accept(); // server bool isActive() const; bool isAuthenticated() const; // login params void setUsername(const QString &s); void setPassword(const QString &s); void setRealm(const QString &s); void continueAfterParams(); // security options (old protocol only uses the first !) void setAllowPlain(bool); void setRequireMutualAuth(bool); void setLocalAddr(const QHostAddress &addr, quint16 port); void close() Q_DECL_OVERRIDE; /** * Are there any messages waiting to be read */ bool transfersAvailable() const Q_DECL_OVERRIDE; /** * Read a message received from the server */ Transfer *read() Q_DECL_OVERRIDE; /** * Send a message to the server */ void write(Request *request) Q_DECL_OVERRIDE; int errorCondition() const Q_DECL_OVERRIDE; QString errorText() const Q_DECL_OVERRIDE; // # QDomElement errorAppSpec() const; // redondo - // extrahttp://bugs.kde.org/show_bug.cgi?id=85158 + // extra https://bugs.kde.org/show_bug.cgi?id=85158 /*# void writeDirect(const QString &s); // must be for debug testing*/ void setNoopTime(int mills); Q_SIGNALS: void connected(); void securityLayerActivated(int); //void needAuthParams(bool user, bool pass, bool realm); void authenticated(); // this signal is ordinarily emitted in processNext void warning(int); // # void incomingXml(const QString &s); // signals emitted in processNext but don't seem to go anywhere... // # void outgoingXml(const QString &s); // // void readyRead(); //signals that there is a transfer ready to be read - defined in stream public Q_SLOTS: void continueAfterWarning(); private Q_SLOTS: void cr_connected(); void cr_error(); /** * collects wire ready outgoing data from the core protocol and sends */ void cp_outgoingData(const QByteArray &); /** * collects parsed incoming data as a transfer from the core protocol and queues */ void cp_incomingData(); void bs_connectionClosed(); void bs_delayedCloseFinished(); void bs_error(int); // server only void ss_readyRead(); void ss_bytesWritten(int); void ss_tlsHandshaken(); void ss_tlsClosed(); void ss_error(int); void doNoop(); void doReadyRead(); private: class Private; Private *const d; void reset(bool all = false); void processNext(); bool handleNeed(); void handleError(); void srvProcessNext(); /** * convert internal method representation to wire */ static char *encode_method(quint8 method); }; #endif diff --git a/protocols/jabber/jabbercontactpool.cpp b/protocols/jabber/jabbercontactpool.cpp index e0ce7b0f1..8c9c066eb 100644 --- a/protocols/jabber/jabbercontactpool.cpp +++ b/protocols/jabber/jabbercontactpool.cpp @@ -1,309 +1,309 @@ /* * jabbercontactpool.cpp * * Copyright (c) 2004 by Till Gerken * Copyright (c) 2006 by Olivier Goffart * * Kopete (c) by the Kopete developers * * ************************************************************************* * * * * * This program is free software; you can redistribute it and/or modify * * * it under the terms of the GNU General Public License as published by * * * the Free Software Foundation; either version 2 of the License, or * * * (at your option) any later version. * * * * * ************************************************************************* */ #include "jabbercontactpool.h" #include "jabber_protocol_debug.h" #include #include #include #include "kopeteuiglobal.h" #include "kopetemetacontact.h" #include "jabberprotocol.h" #include "jabberbasecontact.h" #include "jabbercontact.h" #include "jabbergroupcontact.h" #include "jabbergroupmembercontact.h" #include "jabberresourcepool.h" #include "jabberaccount.h" #include "jabbertransport.h" JabberContactPool::JabberContactPool (JabberAccount *account) { mAccount = account; } JabberContactPool::~JabberContactPool () { qDeleteAll(mPool); } JabberContactPoolItem *JabberContactPool::findPoolItem(const XMPP::RosterItem &contact) { // see if the contact already exists foreach (JabberContactPoolItem *mContactItem, mPool) { if (mContactItem->contact()->rosterItem().jid().full().toLower() == contact.jid().full().toLower()) { return mContactItem; } } return 0; } JabberContact *JabberContactPool::addContact(const XMPP::RosterItem &contact, Kopete::MetaContact *metaContact, bool dirty) { // see if the contact already exists JabberContactPoolItem *mContactItem = findPoolItem(contact); if (mContactItem) { qCDebug(JABBER_PROTOCOL_LOG) << "Updating existing contact " << contact.jid().full() << " - " << mContactItem->contact(); JabberContact *retval = dynamic_cast(mContactItem->contact()); if (!retval) { qCWarning(JABBER_PROTOCOL_LOG) << "ERROR: Wrong contact: " << mContactItem->contact()->contactId() << mContactItem->contact(); KMessageBox::error(Kopete::UI::Global::mainWidget(), "Fatal error in the Jabber contact pool. Please restart Kopete and submit a debug log " - "of your session to http://bugs.kde.org.", + "of your session to https://bugs.kde.org.", QStringLiteral("Fatal Jabber Error")); return 0; } // It exists, update it. mContactItem->contact()->updateContact(contact); mContactItem->setDirty(dirty); return retval; } qCDebug(JABBER_PROTOCOL_LOG) << "Adding new contact " << contact.jid().full(); JabberTransport *transport = nullptr; QString legacyId; //find if the contact should be added to a transport. if (mAccount->transports().contains(contact.jid().domain())) { transport = mAccount->transports()[contact.jid().domain()]; legacyId = transport->legacyId(contact.jid()); } // create new contact instance and add it to the dictionary JabberContact *newContact = new JabberContact(contact, transport ? (Kopete::Account *)transport : (Kopete::Account *)mAccount, metaContact, legacyId); JabberContactPoolItem *newContactItem = new JabberContactPoolItem(newContact); connect(newContact, SIGNAL(contactDestroyed(Kopete::Contact*)), this, SLOT(slotContactDestroyed(Kopete::Contact*))); newContactItem->setDirty(dirty); mPool.append(newContactItem); return newContact; } JabberBaseContact *JabberContactPool::addGroupContact(const XMPP::RosterItem &contact, bool roomContact, Kopete::MetaContact *metaContact, bool dirty) { XMPP::RosterItem mContact(roomContact ? contact.jid().bare() : contact.jid().full()); // see if the contact already exists JabberContactPoolItem *mContactItem = findPoolItem(mContact); if (mContactItem) { if (mContactItem->contact()->inherits(roomContact ? (const char *)("JabberGroupContact") : (const char *)("JabberGroupMemberContact"))) { qCDebug(JABBER_PROTOCOL_LOG) << "Updating existing contact " << mContact.jid().full(); // It exists, update it. mContactItem->contact()->updateContact(mContact); mContactItem->setDirty(dirty); //we must tell to the originating function that no new contact has been added return nullptr;//mContactItem->contact (); } else { //this happen if we receive a MUC invitaiton: when the invitaiton is received, it's from the muc itself //and then kopete will create a temporary contact for it. but it will not be a good contact. qCDebug(JABBER_PROTOCOL_LOG) << "Bad contact will be removed and re-added " << mContact.jid().full(); Kopete::MetaContact *old_mc = mContactItem->contact()->metaContact(); delete mContactItem->contact(); mContactItem = nullptr; if (old_mc->contacts().isEmpty() && old_mc != metaContact) { Kopete::ContactList::self()->removeMetaContact(old_mc); } } } qCDebug(JABBER_PROTOCOL_LOG) << "Adding new contact " << mContact.jid().full(); // create new contact instance and add it to the dictionary JabberBaseContact *newContact; if (roomContact) { newContact = new JabberGroupContact(contact, mAccount, metaContact); } else { newContact = new JabberGroupMemberContact(contact, mAccount, metaContact); } JabberContactPoolItem *newContactItem = new JabberContactPoolItem(newContact); connect(newContact, SIGNAL(contactDestroyed(Kopete::Contact*)), this, SLOT(slotContactDestroyed(Kopete::Contact*))); newContactItem->setDirty(dirty); mPool.append(newContactItem); return newContact; } void JabberContactPool::removeContact(const XMPP::Jid &jid) { qCDebug(JABBER_PROTOCOL_LOG) << "Removing contact " << jid.full(); foreach (JabberContactPoolItem *mContactItem, mPool) { if (mContactItem->contact()->rosterItem().jid().full().toLower() == jid.full().toLower()) { /* * The following deletion will cause slotContactDestroyed() * to be called, which will clean the up the list. */ if (mContactItem->contact() != mAccount->myself()) { Kopete::MetaContact *mc = mContactItem->contact()->metaContact(); delete mContactItem->contact(); if (mc && mc->contacts().isEmpty()) { Kopete::ContactList::self()->removeMetaContact(mc); } } return; } } qCDebug(JABBER_PROTOCOL_LOG) << "WARNING: No match found!"; } void JabberContactPool::slotContactDestroyed(Kopete::Contact *contact) { qCDebug(JABBER_PROTOCOL_LOG) << "Contact deleted, collecting the pieces..."; JabberBaseContact *jabberContact = static_cast(contact); //WARNING this ptr is not usable, we are in the Kopete::Contact destructor // remove contact from the pool foreach (JabberContactPoolItem *mContactItem, mPool) { if (mContactItem->contact() == jabberContact) { JabberContactPoolItem *deletedItem = mPool.takeAt(mPool.indexOf(mContactItem)); delete deletedItem; break; } } // delete all resources for it if (contact->account() == (Kopete::Account *)(mAccount)) { mAccount->resourcePool()->removeAllResources(XMPP::Jid(contact->contactId())); } else { //this is a legacy contact. we have no way to get the real Jid at this point, we can only guess it. QString contactId = contact->contactId().replace('@', '%') + '@' + contact->account()->myself()->contactId(); mAccount->resourcePool()->removeAllResources(XMPP::Jid(contactId)); } } void JabberContactPool::clear() { qCDebug(JABBER_PROTOCOL_LOG) << "Clearing the contact pool."; foreach (JabberContactPoolItem *mContactItem, mPool) { /* * The following deletion will cause slotContactDestroyed() * to be called, which will clean the up the list. * NOTE: this is a very inefficient way to clear the list */ delete mContactItem->contact(); } } void JabberContactPool::setDirty(const XMPP::Jid &jid, bool dirty) { qCDebug(JABBER_PROTOCOL_LOG) << "Setting flag for " << jid.full() << " to " << dirty; foreach (JabberContactPoolItem *mContactItem, mPool) { if (mContactItem->contact()->rosterItem().jid().full().toLower() == jid.full().toLower()) { mContactItem->setDirty(dirty); return; } } qCDebug(JABBER_PROTOCOL_LOG) << "WARNING: No match found!"; } void JabberContactPool::cleanUp() { qCDebug(JABBER_PROTOCOL_LOG) << "Cleaning dirty items from contact pool."; foreach (JabberContactPoolItem *mContactItem, mPool) { if (mContactItem->dirty()) { qCDebug(JABBER_PROTOCOL_LOG) << "Removing dirty contact " << mContactItem->contact()->contactId(); /* * The following deletion will cause slotContactDestroyed() * to be called, which will clean the up the list. */ delete mContactItem->contact(); } } } JabberBaseContact *JabberContactPool::findExactMatch(const XMPP::Jid &jid) { foreach (JabberContactPoolItem *mContactItem, mPool) { if (mContactItem->contact()->rosterItem().jid().full().toLower() == jid.full().toLower()) { return mContactItem->contact(); } } return nullptr; } JabberBaseContact *JabberContactPool::findRelevantRecipient(const XMPP::Jid &jid) { foreach (JabberContactPoolItem *mContactItem, mPool) { if (mContactItem->contact()->rosterItem().jid().full().toLower() == jid.bare().toLower()) { return mContactItem->contact(); } } return nullptr; } QList JabberContactPool::findRelevantSources(const XMPP::Jid &jid) { QList list; foreach (JabberContactPoolItem *mContactItem, mPool) { if (mContactItem->contact()->rosterItem().jid().bare().toLower() == jid.bare().toLower()) { list.append(mContactItem->contact()); } } return list; } JabberContactPoolItem::JabberContactPoolItem (JabberBaseContact *contact) { mDirty = true; mContact = contact; } JabberContactPoolItem::~JabberContactPoolItem () { } void JabberContactPoolItem::setDirty(bool dirty) { mDirty = dirty; } bool JabberContactPoolItem::dirty() { return mDirty; } JabberBaseContact *JabberContactPoolItem::contact() { return mContact; } diff --git a/protocols/oscar/oscaraccount.cpp b/protocols/oscar/oscaraccount.cpp index 30e39484b..92eed2dcf 100644 --- a/protocols/oscar/oscaraccount.cpp +++ b/protocols/oscar/oscaraccount.cpp @@ -1,1519 +1,1519 @@ /* oscaraccount.cpp - Oscar Account Class Copyright (c) 2002 by Tom Linsky Copyright (c) 2002 by Chris TenHarmsel Copyright (c) 2004 by Matt Rogers Copyright (c) 2008 by Roman Jarosz Kopete (c) 2002-2008 by the Kopete developers ************************************************************************* * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ************************************************************************* */ #include "oscaraccount.h" #include "kopetepassword.h" #include "kopeteprotocol.h" #include "kopetemetacontact.h" #include "kopetecontactlist.h" #include "kopeteidentity.h" #include "kopetegroup.h" #include "kopeteuiglobal.h" #include "kopetecontact.h" #include "kopetechatsession.h" #include #include #include #include #include #include #include #include #include #include #include #include // Qt::escape #include #include #include #include #include #include #include #include #include #include "client.h" #include "connection.h" #include "oscartypeclasses.h" #include "oscarmessage.h" #include "oscarutils.h" #include "oscarclientstream.h" #include "contactmanager.h" #include "oscarlistnonservercontacts.h" #include "kopetetransfermanager.h" #include "kopeteversion.h" #include "oscarversionupdater.h" #include "filetransferhandler.h" #include "chatroomhandler.h" #include "nscainfoevent.h" #include "oscarpresence.h" #include "oscarprotocol.h" #include "oscarstatusmanager.h" #include #include using namespace Oscar; class OscarAccountPrivate : public Client::CodecProvider { // Backreference OscarAccount& account; public: OscarAccountPrivate( OscarAccount& a ): account( a ) {} //The liboscar hook for the account Client* engine; quint32 ssiLastModTime; //contacts waiting on SSI add ack and their metacontact QMap addContactMap; //contacts waiting on their group to be added QMap contactAddQueue; QMap contactChangeQueue; QMap fileTransferHandlerMap; OscarListNonServerContacts* olnscDialog; unsigned int versionUpdaterStamp; bool versionAlreadyUpdated; bool buddyIconDirty; QTextCodec* codecForContact( const QString& contactName ) const Q_DECL_OVERRIDE { return account.contactCodec( Oscar::normalize( contactName ) ); } QTextCodec* codecForAccount() const Q_DECL_OVERRIDE { return account.defaultCodec(); } }; OscarAccount::OscarAccount(Kopete::Protocol *parent, const QString &accountID, bool isICQ) : Kopete::PasswordedAccount( parent, accountID, false ) { kDebug(OSCAR_GEN_DEBUG) << " accountID='" << accountID << "', isICQ=" << isICQ << endl; d = new OscarAccountPrivate( *this ); d->engine = new Client( this ); QObject::connect( d->engine, SIGNAL(createClientStream(ClientStream**)), this, SLOT(createClientStream(ClientStream**)) ); d->engine->setIsIcq( isICQ ); // Set version capability // last 4 bytes determine version // first number, major version // second number, minor version // third number, point version 100+ // fourth number, point version 0-99 QByteArray kg( "Kopete ICQ ", 16 ); kg[12] = KOPETE_VERSION_MAJOR; kg[13] = KOPETE_VERSION_MINOR; kg[14] = KOPETE_VERSION_RELEASE / 100; kg[15] = KOPETE_VERSION_RELEASE % 100; d->engine->setVersionCap( kg ); d->versionAlreadyUpdated = false; d->buddyIconDirty = false; d->versionUpdaterStamp = OscarVersionUpdater::self()->stamp(); if ( isICQ ) d->engine->setVersion( OscarVersionUpdater::self()->getICQVersion() ); else d->engine->setVersion( OscarVersionUpdater::self()->getAIMVersion() ); d->engine->setCodecProvider( d ); d->olnscDialog = nullptr; QObject::connect( d->engine, SIGNAL(loggedIn()), this, SLOT(loginActions()) ); QObject::connect( d->engine, SIGNAL(messageReceived(Oscar::Message)), this, SLOT(messageReceived(Oscar::Message)) ); QObject::connect( d->engine, SIGNAL(socketError(int,QString)), this, SLOT(slotSocketError(int,QString)) ); QObject::connect( d->engine, SIGNAL(taskError(Oscar::SNAC,int,bool)), this, SLOT(slotTaskError(Oscar::SNAC,int,bool)) ); QObject::connect( d->engine, SIGNAL(userStartedTyping(QString)), this, SLOT(userStartedTyping(QString)) ); QObject::connect( d->engine, SIGNAL(userStoppedTyping(QString)), this, SLOT(userStoppedTyping(QString)) ); QObject::connect( d->engine, SIGNAL(iconNeedsUploading()), this, SLOT(slotSendBuddyIcon()) ); QObject::connect( d->engine, SIGNAL(incomingFileTransfer(FileTransferHandler*)), this, SLOT(incomingFileTransfer(FileTransferHandler*)) ); QObject::connect( d->engine, SIGNAL(chatroomRequest(ChatRoomHandler*)), this, SLOT(chatroomRequest(ChatRoomHandler*)) ); Kopete::TransferManager *tm = Kopete::TransferManager::transferManager(); QObject::connect( tm, SIGNAL(refused(Kopete::FileTransferInfo)), this, SLOT(fileTransferRefused(Kopete::FileTransferInfo)) ); QObject::connect( tm, SIGNAL(accepted(Kopete::Transfer*,QString)), this, SLOT(fileTransferAccept(Kopete::Transfer*,QString)) ); } OscarAccount::~OscarAccount() { OscarAccount::disconnect(); delete d; } Client* OscarAccount::engine() { return d->engine; } void OscarAccount::logOff( Kopete::Account::DisconnectReason reason ) { kDebug(OSCAR_GEN_DEBUG) << "accountId='" << accountId() << "'"; //disconnect the signals Kopete::ContactList* kcl = Kopete::ContactList::self(); QObject::disconnect( kcl, SIGNAL(groupRenamed(Kopete::Group*,QString)), this, SLOT(kopeteGroupRenamed(Kopete::Group*,QString)) ); QObject::disconnect( kcl, SIGNAL(groupRemoved(Kopete::Group*)), this, SLOT(kopeteGroupRemoved(Kopete::Group*)) ); QObject::disconnect( d->engine->ssiManager(), SIGNAL(contactAdded(OContact)), this, SLOT(ssiContactAdded(OContact)) ); QObject::disconnect( d->engine->ssiManager(), SIGNAL(groupAdded(OContact)), this, SLOT(ssiGroupAdded(OContact)) ); QObject::disconnect( d->engine->ssiManager(), SIGNAL(groupUpdated(OContact)), this, SLOT(ssiGroupUpdated(OContact)) ); QObject::disconnect( d->engine->ssiManager(), SIGNAL(contactUpdated(OContact)), this, SLOT(ssiContactUpdated(OContact)) ); d->engine->close(); OscarProtocol* p = dynamic_cast(protocol()); if ( myself() && p && p->statusManager() ) myself()->setOnlineStatus( p->statusManager()->onlineStatusOf( Oscar::Presence( Oscar::Presence::Offline ) ) ); d->contactAddQueue.clear(); d->contactChangeQueue.clear(); disconnected( reason ); } void OscarAccount::disconnect() { logOff( Kopete::Account::Manual ); } bool OscarAccount::passwordWasWrong() { return password().isWrong(); } bool OscarAccount::setIdentity( Kopete::Identity *ident ) { if ( !Kopete::PasswordedAccount::setIdentity( ident ) ) return false; QObject::connect( ident, SIGNAL(propertyChanged(Kopete::PropertyContainer*,QString,QVariant,QVariant)), this, SLOT(slotIdentityPropertyChanged(Kopete::PropertyContainer*,QString,QVariant,QVariant)) ); QString photoPath = ident->property( Kopete::Global::Properties::self()->photo() ).value().toString(); updateBuddyIcon( photoPath ); return true; } void OscarAccount::loginActions() { password().setWrong( false ); kDebug(OSCAR_GEN_DEBUG) << "processing SSI list"; processSSIList(); //start a chat nav connection if ( !engine()->isIcq() ) { kDebug(OSCAR_GEN_DEBUG) << "sending request for chat nav service"; d->engine->requestServerRedirect( 0x000D ); } kDebug(OSCAR_RAW_DEBUG) << "sending request for icon service"; d->engine->connectToIconServer(); if ( d->buddyIconDirty ) updateBuddyIconInSSI(); } void OscarAccount::processSSIList() { //disconnect signals so we don't attempt to add things to SSI! Kopete::ContactList* kcl = Kopete::ContactList::self(); QObject::disconnect( kcl, SIGNAL(groupRenamed(Kopete::Group*,QString)), this, SLOT(kopeteGroupRenamed(Kopete::Group*,QString)) ); QObject::disconnect( kcl, SIGNAL(groupRemoved(Kopete::Group*)), this, SLOT(kopeteGroupRemoved(Kopete::Group*)) ); kDebug(OSCAR_RAW_DEBUG) ; ContactManager* listManager = d->engine->ssiManager(); //first add groups QList groupList = listManager->groupList(); QList::const_iterator git = groupList.constBegin(); QList::const_iterator listEnd = groupList.constEnd(); //the protocol dictates that there is at least one group that has contacts //so i don't have to check for an empty group list kDebug(OSCAR_GEN_DEBUG) << "Adding " << groupList.count() << " groups to contact list"; for( ; git != listEnd; ++git ) { //add all the groups. if ( ( *git ).name() == "Buddies" ) continue; kDebug( OSCAR_GEN_DEBUG ) << "Adding SSI group'" << ( *git ).name() << "' to the kopete contact list" << endl; kcl->findGroup( ( *git ).name() ); } //then add contacts QList contactList = listManager->contactList(); QList::const_iterator bit = contactList.constBegin(); QList::const_iterator blistEnd = contactList.constEnd(); kDebug(OSCAR_GEN_DEBUG) << "Adding " << contactList.count() << " contacts to contact list"; for ( ; bit != blistEnd; ++bit ) { OContact groupForAdd = listManager->findGroup( ( *bit ).gid() ); Kopete::Group* group; if ( groupForAdd.isValid() && groupForAdd.name() != "Buddies" ) group = kcl->findGroup( groupForAdd.name() ); //add if not present else group = Kopete::Group::topLevel(); kDebug( OSCAR_GEN_DEBUG ) << "Adding contact '" << ( *bit ).name() << "' to kopete list in group " << group->displayName() << endl; OscarContact* oc = dynamic_cast( contacts().value( ( *bit ).name() ) ); if ( oc ) { OContact item = ( *bit ); oc->setSSIItem( item ); //only synchronizes group if metacontact is a member of //a single group if ( oc->metaContact()->groups().size() == 1 ) { Kopete::Group* oldGrp = oc->metaContact()->groups().first(); if ( oldGrp->displayName() != group->displayName() && oc->metaContact()->contacts().count() == 1 ) { oc->metaContact()->moveToGroup( oldGrp, group ); } } } else addContact( ( *bit ).name(), QString(), group, Kopete::Account::DontChangeKABC ); } QObject::connect( kcl, SIGNAL(groupRenamed(Kopete::Group*,QString)), this, SLOT(kopeteGroupRenamed(Kopete::Group*,QString)) ); QObject::connect( kcl, SIGNAL(groupRemoved(Kopete::Group*)), this, SLOT(kopeteGroupRemoved(Kopete::Group*)) ); QObject::connect( listManager, SIGNAL(contactAdded(OContact)), this, SLOT(ssiContactAdded(OContact)) ); QObject::connect( listManager, SIGNAL(groupAdded(OContact)), this, SLOT(ssiGroupAdded(OContact)) ); QObject::connect( listManager, SIGNAL(groupUpdated(OContact)), this, SLOT(ssiGroupUpdated(OContact)) ); QObject::connect( listManager, SIGNAL(contactUpdated(OContact)), this, SLOT(ssiContactUpdated(OContact)) ); // TODO: Synchronize groups. // Currently groups that have been removed from the server do not get // removed from the client's list. The problem is that a group can hold // contacts from other protocols. Perhaps groups should store which // protocols are using it. Asking the user for which account to create // a group, similar to how contact addition, could work. const QHash &nonServerContacts = contacts(); QHash::ConstIterator it = nonServerContacts.constBegin(); QStringList nonServerContactList; for ( ; it != nonServerContacts.constEnd(); ++it ) { const OscarContact* oc = dynamic_cast( ( *it ) ); if ( !oc ) continue; kDebug(OSCAR_GEN_DEBUG) << oc->contactId() << " contact ssi type: " << oc->ssiItem().type(); if ( !oc->isOnServer() ) nonServerContactList.append( ( *it )->contactId() ); } kDebug(OSCAR_GEN_DEBUG) << "the following contacts are not on the server side list" << nonServerContactList << endl; bool showMissingContactsDialog = configGroup()->readEntry(QString::fromLatin1("ShowMissingContactsDialog"), true); if ( !nonServerContactList.isEmpty() && showMissingContactsDialog ) { d->olnscDialog = new OscarListNonServerContacts( Kopete::UI::Global::mainWidget() ); QObject::connect( d->olnscDialog, SIGNAL(closing()), this, SLOT(nonServerAddContactDialogClosed()) ); d->olnscDialog->addContacts( nonServerContactList ); d->olnscDialog->show(); } } void OscarAccount::nonServerAddContactDialogClosed() { if ( !d->olnscDialog ) return; if ( d->olnscDialog->result() == KDialog::Yes ) { NonServerContactsAddInfoEvent *event = new NonServerContactsAddInfoEvent( d->engine->ssiManager(), engine()->isIcq(), this ); event->sendEvent(); //start adding contacts kDebug(OSCAR_GEN_DEBUG) << "adding non server contacts to the contact list"; //get the contact list. get the OscarContact object, then the group //check if the group is on ssi, if not, add it //if so, add the contact. QStringList offliners = d->olnscDialog->nonServerContactList(); QStringList::iterator it, itEnd = offliners.end(); for ( it = offliners.begin(); it != itEnd; ++it ) { OscarContact* oc = dynamic_cast( contacts().value( ( *it ) ) ); if ( !oc ) { kDebug(OSCAR_GEN_DEBUG) << "no OscarContact object available for" << ( *it ); continue; } Kopete::MetaContact* mc = oc->metaContact(); if ( !mc ) { kDebug(OSCAR_GEN_DEBUG) << "no metacontact object available for" << oc->contactId(); continue; } Kopete::Group* group = mc->groups().first(); if ( !group ) { kDebug(OSCAR_GEN_DEBUG) << "no metacontact object available for" << oc->contactId(); continue; } event->addContact( *it ); addContactToSSI( ( *it ), group->displayName(), true ); } } else if ( d->olnscDialog->result() == KDialog::No ) { //remove contacts kDebug( OSCAR_GEN_DEBUG ) << "removing non server contacts from the " "contact list"; Kopete::ContactList* kcl = Kopete::ContactList::self(); QStringList offliners = d->olnscDialog->nonServerContactList(); QStringList::iterator it, itEnd = offliners.end(); for ( it = offliners.begin(); it != itEnd; ++it ) { OscarContact* oc = dynamic_cast( contacts().value( (*it) ) ); if ( !oc ) { kDebug( OSCAR_GEN_DEBUG ) << "no OscarContact object available " "for" << ( *it ) << endl; continue; } Kopete::MetaContact* mc = oc->metaContact(); if ( !mc ) { kDebug( OSCAR_GEN_DEBUG ) << "no metacontact object available " "for" << ( oc->contactId() ) << endl; continue; } if ( oc->metaContact()->contacts().count() <= 1 ) { kcl->removeMetaContact( oc->metaContact() ); } else { kDebug( OSCAR_GEN_DEBUG ) << oc->contactId() << " metacontact " "contains multiple contacts."; } } } bool showOnce = d->olnscDialog->onlyShowOnce(); configGroup()->writeEntry( QString::fromLatin1("ShowMissingContactsDialog") , !showOnce); configGroup()->sync(); d->olnscDialog->deleteLater(); d->olnscDialog = nullptr; } void OscarAccount::chatroomRequest( ChatRoomHandler* handler ) { KGuiItem buttonYes( KStandardGuiItem::yes() ); KGuiItem buttonNo( KStandardGuiItem::no() ); buttonYes.setText( i18nc( "@action:button filter-yes", "%1", KStandardGuiItem::yes().text() ) ); buttonNo.setText( i18nc( "@action:button filter-no", "%1", KStandardGuiItem::no().text() ) ); i18nc( "@action:button post-filter", "." ); QMessageBox *dialog = new QMessageBox( QMessageBox::Question, i18n( "Chat Room Invitation" ), ( handler->contact() + ": " + handler->invite() ), QMessageBox::Yes | QMessageBox::No, NULL ); dialog->setObjectName( "questionYesNoCancel" ); dialog->setDefaultButton(QMessageBox::Yes); dialog->setModal( false ); QObject::connect( dialog->button(QMessageBox::Yes), SIGNAL(clicked()), handler, SLOT(accept()) ); QObject::connect( dialog->button(QMessageBox::No), SIGNAL(clicked()), handler, SLOT(reject()) ); QObject::connect( handler, SIGNAL(joinChatRoom(QString,int)), engine(), SLOT(joinChatRoom(QString,int)) ); dialog->show(); dialog->raise(); dialog->activateWindow(); } void OscarAccount::incomingFileTransfer( FileTransferHandler* ftHandler ) { QString sender = Oscar::normalize( ftHandler->contact() ); if ( !contacts().value( sender ) ) { kDebug(OSCAR_RAW_DEBUG) << "Adding '" << sender << "' as temporary contact"; addContact( sender, QString(), 0, Kopete::Account::Temporary ); } Kopete::Contact * ct = contacts().value( sender ); // Fill the fileNameList with empty filenames if we have more files so the kopete transfer knows about them QStringList fileNameList; fileNameList << ftHandler->fileName(); for ( int i = 1; i < ftHandler->fileCount(); i++ ) fileNameList << ""; Kopete::TransferManager* tm = Kopete::TransferManager::transferManager(); uint ftId = tm->askIncomingTransfer( ct, fileNameList, ftHandler->totalSize(), ftHandler->description(), ftHandler->internalId(), QPixmap() ); QObject::connect( ftHandler, SIGNAL(destroyed(QObject*)), this, SLOT(fileTransferDestroyed(QObject*)) ); QObject::connect( ftHandler, SIGNAL(transferCancelled()), this, SLOT(fileTransferCancelled()) ); d->fileTransferHandlerMap.insert( ftId, ftHandler ); } void OscarAccount::fileTransferDestroyed( QObject* object ) { uint key = d->fileTransferHandlerMap.key( (FileTransferHandler*)object, 0 ); if ( key > 0 ) d->fileTransferHandlerMap.remove( key ); else kDebug(OSCAR_GEN_DEBUG) << "FileTransferHandler not in the map!!!"; } void OscarAccount::fileTransferCancelled() { FileTransferHandler* ftHandler = qobject_cast(sender()); if ( !ftHandler ) return; uint key = d->fileTransferHandlerMap.key( ftHandler, 0 ); if ( key == 0 ) { kDebug(OSCAR_GEN_DEBUG) << "FileTransferHandler not in the map!!!"; return; } QObject::disconnect( ftHandler, SIGNAL(transferCancelled()), this, SLOT(fileTransferCancelled()) ); Kopete::TransferManager::transferManager()->cancelIncomingTransfer( key ); } void OscarAccount::fileTransferRefused( const Kopete::FileTransferInfo& info ) { FileTransferHandler* ftHandler = d->fileTransferHandlerMap.value( info.transferId(), 0 ); if ( !ftHandler ) return; QObject::disconnect( ftHandler, SIGNAL(transferCancelled()), this, SLOT(fileTransferCancelled()) ); ftHandler->cancel(); } void OscarAccount::fileTransferAccept( Kopete::Transfer* transfer, const QString& fileName ) { FileTransferHandler* ftHandler = d->fileTransferHandlerMap.value( transfer->info().transferId(), 0 ); if ( !ftHandler ) return; QObject::disconnect( ftHandler, SIGNAL(transferCancelled()), this, SLOT(fileTransferCancelled()) ); QObject::connect( transfer, SIGNAL(transferCanceled()), ftHandler, SLOT(cancel()) ); QObject::connect( ftHandler, SIGNAL(transferCancelled()), transfer, SLOT(slotCancelled()) ); QObject::connect( ftHandler, SIGNAL(transferError(int,QString)), transfer, SLOT(slotError(int,QString)) ); QObject::connect( ftHandler, SIGNAL(transferProcessed(uint)), transfer, SLOT(slotProcessed(uint)) ); QObject::connect( ftHandler, SIGNAL(transferFinished()), transfer, SLOT(slotComplete()) ); QObject::connect( ftHandler, SIGNAL(transferNextFile(QString,QString)), transfer, SLOT(slotNextFile(QString,QString)) ); if ( transfer->info().saveToDirectory() ) ftHandler->save( fileName ); else ftHandler->saveAs( QStringList() << fileName ); } void OscarAccount::kopeteGroupRemoved( Kopete::Group* group ) { if ( isConnected() && group->displayName() != "Buddies" ) d->engine->removeGroup( group->displayName() ); } void OscarAccount::kopeteGroupAdded( Kopete::Group* group ) { if ( isConnected() ) d->engine->addGroup( group->displayName() ); } void OscarAccount::kopeteGroupRenamed( Kopete::Group* group, const QString& oldName ) { if ( isConnected() && oldName != "Buddies" ) d->engine->renameGroup( oldName, group->displayName() ); } void OscarAccount::messageReceived( const Oscar::Message& message ) { //the message isn't for us somehow if ( Oscar::normalize( message.receiver() ) != Oscar::normalize( accountId() ) ) { kDebug(OSCAR_RAW_DEBUG) << "got a message but we're not the receiver: " << message.textArray() << endl; return; } /* Logic behind this: * If we don't have the contact yet, create it as a temporary * Create the message manager * Get the sanitized message back * Append to the chat window */ QString sender = Oscar::normalize( message.sender() ); if ( !contacts().value( sender ) ) { kDebug(OSCAR_RAW_DEBUG) << "Adding '" << sender << "' as temporary contact"; addContact( sender, QString(), 0, Kopete::Account::Temporary ); } OscarContact* ocSender = static_cast ( contacts().value( sender ) ); //should exist now if ( !ocSender ) { kWarning(OSCAR_RAW_DEBUG) << "Temporary contact creation failed for '" << sender << "'! Discarding message: " << message.textArray() << endl; return; } else { if ( message.hasProperty( Oscar::Message::WWP ) ) ocSender->setNickName( i18n("ICQ Web Express") ); if ( message.hasProperty( Oscar::Message::EMail ) ) ocSender->setNickName( i18n("ICQ Email Express") ); } Kopete::ChatSession* chatSession = ocSender->manager( Kopete::Contact::CanCreate ); chatSession->receivedTypingMsg( ocSender, false ); //person is done typing //decode message QString realText( message.text( contactCodec( ocSender ) ) ); //sanitize; QString sanitizedMsg = sanitizedMessage( realText ); Kopete::ContactPtrList me; me.append( myself() ); Kopete::Message chatMessage( ocSender, me ); chatMessage.setHtmlBody( sanitizedMsg ); chatMessage.setTimestamp( message.timestamp() ); chatMessage.setDirection( Kopete::Message::Inbound ); chatSession->appendMessage( chatMessage ); } QString OscarAccount::sanitizedMessage( const QString& message ) const { QDomDocument doc; QString domError; int errLine = 0, errCol = 0; QString msg = addQuotesAroundAttributes(message); msg = makeWellFormedXML( msg ); // Official AIM client send crap so we have to sort it out. msg.replace( "
", "
", Qt::CaseInsensitive ); doc.setContent( msg, false, &domError, &errLine, &errCol ); if ( !domError.isEmpty() ) //error parsing, do nothing { kDebug(OSCAR_AIM_DEBUG) << "error from dom document conversion: " << domError << "line:" << errLine << "col:" << errCol; // HACK: for trillian which sends totaly mangled html (there are not ended font tags) if ( message.indexOf( QRegExp( "[\\s]*<[\\s]*HTML[\\s]*>[\\s]*<[\\s]*BODY", Qt::CaseInsensitive ) ) != 0 ) return sanitizedPlainMessage( message ); else return message; } else { kDebug(OSCAR_AIM_DEBUG) << "conversion to dom document successful." << "looking for font tags" << endl; QList fontTagList = getElementsByTagNameCI( doc, "FONT" ); if ( fontTagList.count() == 0 ) { if ( message.indexOf( QRegExp( "[\\s]*<[\\s]*HTML[\\s]*>[\\s]*<[\\s]*BODY", Qt::CaseInsensitive ) ) != 0 ) { kDebug(OSCAR_AIM_DEBUG) << "No html tags found. Returning normal message"; return sanitizedPlainMessage( message ); } } else { kDebug(OSCAR_AIM_DEBUG) << "Found font tags. Attempting replacement"; uint numFontTags = fontTagList.count(); for ( uint i = 0; i < numFontTags; i++ ) { QDomNode fontNode = fontTagList.at(i); QDomElement fontEl; if ( !fontNode.isNull() && fontNode.isElement() ) fontEl = fontNode.toElement(); else continue; if ( fontEl.hasAttribute( "BACK" ) ) { QString backgroundColor = fontEl.attribute( "BACK" ); backgroundColor.insert( 0, "background-color: " ); backgroundColor.append( ';' ); fontEl.setAttribute( "style", backgroundColor ); fontEl.removeAttribute( "BACK" ); } } } } kDebug(OSCAR_AIM_DEBUG) << "sanitized message is " << doc.toString(); return doc.toString(); } void OscarAccount::setServerAddress(const QString &server) { configGroup()->writeEntry( QString::fromLatin1( "Server" ), server ); } void OscarAccount::setServerPort(int port) { if (port<=0) port=5190; configGroup()->writeEntry( QString::fromLatin1( "Port" ), port); } void OscarAccount::setServerEncrypted( bool encrypted ) { configGroup()->writeEntry( QString::fromLatin1( "Encrypted" ), encrypted); } void OscarAccount::setProxyServerSocks5( bool enable ) { configGroup()->writeEntry( QString::fromLatin1( "ProxySocks5" ), enable ); } void OscarAccount::setProxyServerAddress(const QString &server) { configGroup()->writeEntry( QString::fromLatin1( "ProxyServer" ), server ); } void OscarAccount::setProxyServerPort(int port) { configGroup()->writeEntry( QString::fromLatin1( "ProxyPort" ), port); } void OscarAccount::setProxyServerEnabled(bool enable) { configGroup()->writeEntry( QString::fromLatin1( "ProxyEnable" ), enable); } QTextCodec* OscarAccount::defaultCodec() const { QTextCodec* codec = QTextCodec::codecForMib( configGroup()->readEntry( "DefaultEncoding", 4 ) ); if ( codec ) return codec; else return QTextCodec::codecForMib( 4 ); } QTextCodec* OscarAccount::contactCodec( const OscarContact* contact ) const { if ( contact ) return contact->contactCodec(); else return defaultCodec(); } QTextCodec* OscarAccount::contactCodec( const QString& contactName ) const { // XXX Need const_cast because Kopete::Account::contacts() // XXX method is not const for some strange reason. OscarContact* contact = static_cast ( const_cast(this)->contacts().value( contactName ) ); return contactCodec( contact ); } void OscarAccount::updateBuddyIcon( const QString &path ) { myself()->removeProperty( Kopete::Global::Properties::self()->photo() ); if ( !path.isEmpty() ) { QImage image( path ); if ( image.isNull() ) return; const QSize size = ( d->engine->isIcq() ) ? QSize( 52, 64 ) : QSize( 48, 48 ); image = image.scaled( size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation ); if( image.width() > size.width()) image = image.copy( ( image.width() - size.width() ) / 2, 0, size.width(), image.height() ); if( image.height() > size.height()) image = image.copy( 0, ( image.height() - size.height() ) / 2, image.width(), size.height() ); QString newlocation( QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + "oscarpictures/" + accountId() + ".jpg" ) ; kDebug(OSCAR_RAW_DEBUG) << "Saving buddy icon: " << newlocation; if ( !image.save( newlocation, "JPEG" ) ) return; myself()->setProperty( Kopete::Global::Properties::self()->photo() , newlocation ); } d->buddyIconDirty = true; updateBuddyIconInSSI(); } bool OscarAccount::addContactToSSI( const QString& contactName, const QString& groupName, bool autoAddGroup ) { ContactManager* listManager = d->engine->ssiManager(); if ( !listManager->findGroup( groupName ) ) { if ( !autoAddGroup ) return false; kDebug(OSCAR_GEN_DEBUG) << "adding non-existent group " << groupName << endl; d->contactAddQueue[Oscar::normalize( contactName )] = groupName; d->engine->addGroup( groupName ); } else { d->engine->addContact( contactName, groupName ); } return true; } bool OscarAccount::changeContactGroupInSSI( const QString& contact, const QString& newGroupName, bool autoAddGroup ) { ContactManager* listManager = d->engine->ssiManager(); if ( !listManager->findGroup( newGroupName ) ) { if ( !autoAddGroup ) return false; kDebug(OSCAR_GEN_DEBUG) << "adding non-existent group " << newGroupName << endl; d->contactChangeQueue[Oscar::normalize( contact )] = newGroupName; d->engine->addGroup( newGroupName ); } else { d->engine->changeContactGroup( contact, newGroupName ); } return true; } bool OscarAccount::createContact(const QString &contactId, Kopete::MetaContact *parentContact) { /* We're not even online or connecting * (when getting server contacts), so don't bother */ if ( !engine()->isActive() ) { kDebug(OSCAR_GEN_DEBUG) << "Can't add contact, we are offline!"; return false; } /* Logic for SSI additions If the contact is temporary, no SSI addition at all. Just create the contact and be done with it If the contact is not temporary, we need to do the following: 1. Check if contact already exists in the SSI manager, if so, just create the contact 2. If contact doesn't exist: 2.a. create group on SSI if needed 2.b. create contact on SSI 2.c. create kopete contact */ QList dummyList; if ( parentContact->isTemporary() ) { OContact tempItem( contactId, 0, 0, 0xFFFF, dummyList, 0 ); return createNewContact( contactId, parentContact, tempItem ); } OContact ssiItem = d->engine->ssiManager()->findContact( contactId ); if ( ssiItem ) { kDebug(OSCAR_GEN_DEBUG) << "Have new SSI entry. Finding contact"; if ( contacts().value( ssiItem.name() ) ) { kDebug(OSCAR_GEN_DEBUG) << "Found contact in list. Updating SSI item"; OscarContact* oc = static_cast( contacts().value( ssiItem.name() ) ); oc->setSSIItem( ssiItem ); return true; } else { kDebug(OSCAR_GEN_DEBUG) << "Didn't find contact in list, creating new contact"; return createNewContact( contactId, parentContact, ssiItem ); } } else { //new contact, check temporary, if temporary, don't add to SSI. otherwise, add. kDebug(OSCAR_GEN_DEBUG) << "New contact '" << contactId << "' not in SSI." << " Creating new contact" << endl; kDebug(OSCAR_GEN_DEBUG) << "Adding " << contactId << " to server side list"; QString groupName; Kopete::GroupList kopeteGroups = parentContact->groups(); //get the group list if ( kopeteGroups.isEmpty() || kopeteGroups.first() == Kopete::Group::topLevel() ) { kDebug(OSCAR_GEN_DEBUG) << "Contact with NO group. " << "Adding to group 'Buddies'"; groupName = "Buddies"; } else { //apparently kopeteGroups.first() can be invalid. Attempt to prevent //crashes in SSIData::findGroup(const QString& name) groupName = kopeteGroups.first() ? kopeteGroups.first()->displayName() : "Buddies"; kDebug(OSCAR_GEN_DEBUG) << "Contact with group." << " No. of groups = " << kopeteGroups.count() << " Name of first group = " << groupName << endl; } if( groupName.isEmpty() ) { // emergency exit, should never occur kWarning(OSCAR_GEN_DEBUG) << "Could not add contact because no groupname was given"; return false; } d->addContactMap[Oscar::normalize( contactId )] = parentContact; addContactToSSI( Oscar::normalize( contactId ), groupName, true ); return true; } } void OscarAccount::updateVersionUpdaterStamp() { d->versionUpdaterStamp = OscarVersionUpdater::self()->stamp(); } void OscarAccount::ssiContactAdded( const OContact& item ) { QString normalizedName = Oscar::normalize( item.name() ); if ( contacts().value( item.name() ) ) { kDebug(OSCAR_GEN_DEBUG) << "Received confirmation from server. modifying " << item.name(); OscarContact* oc = static_cast( contacts().value( item.name() ) ); oc->setSSIItem( item ); // To be safe remove contact from addContactMap d->addContactMap.remove( normalizedName ); } else if ( d->addContactMap.contains( normalizedName ) ) { kDebug(OSCAR_GEN_DEBUG) << "Received confirmation from server. adding " << item.name() << " to the contact list" << endl; OscarContact* oc = createNewContact( item.name(), d->addContactMap[normalizedName], item ); d->addContactMap.remove( normalizedName ); if ( oc && oc->ssiItem().waitingAuth() ) QTimer::singleShot( 1, oc, SLOT(requestAuthorization()) ); } else kDebug(OSCAR_GEN_DEBUG) << "Got addition for contact we weren't waiting on"; } void OscarAccount::ssiGroupAdded( const OContact& item ) { //check the contact add queue for any contacts matching the //group name we just added kDebug(OSCAR_GEN_DEBUG) << "Looking for contacts to be added in group " << item.name(); QMap::iterator it; for ( it = d->contactAddQueue.begin(); it != d->contactAddQueue.end(); ++it ) { if ( Oscar::normalize( it.value() ) == Oscar::normalize( item.name() ) ) { kDebug(OSCAR_GEN_DEBUG) << "starting delayed add of contact '" << it.key() << "' to group " << item.name() << endl; d->engine->addContact( Oscar::normalize( it.key() ), item.name() ); d->contactAddQueue.erase( it ); } } for ( it = d->contactChangeQueue.begin(); it != d->contactChangeQueue.end(); ++it ) { if ( Oscar::normalize( it.value() ) == Oscar::normalize( item.name() ) ) { kDebug(OSCAR_GEN_DEBUG) << "starting delayed change of contact '" << it.key() << "' to group " << item.name() << endl; d->engine->changeContactGroup( it.key(), item.name() ); d->contactChangeQueue.erase( it ); } } } void OscarAccount::ssiContactUpdated( const OContact& item ) { Kopete::Contact* contact = contacts().value( item.name() ); if ( !contact ) return; kDebug(OSCAR_RAW_DEBUG) << "Updating SSI Item"; OscarContact* oc = static_cast( contact ); oc->setSSIItem( item ); } void OscarAccount::userStartedTyping( const QString & contact ) { Kopete::Contact * ct = contacts().value( Oscar::normalize( contact ) ); if ( ct ) { OscarContact * oc = static_cast( ct ); oc->startedTyping(); } } void OscarAccount::userStoppedTyping( const QString & contact ) { Kopete::Contact * ct = contacts().value( Oscar::normalize( contact ) ); if ( ct ) { OscarContact * oc = static_cast( ct ); oc->stoppedTyping(); } } void OscarAccount::slotSocketError( int errCode, const QString& errString ) { Q_UNUSED( errCode ); if ( !isBusy() ) KNotification::event( QLatin1String("connection_error"), i18nc( "account has been disconnected", "Kopete: %1 disconnected", accountId() ), errString, myself()->onlineStatus().protocolIcon(KIconLoader::SizeMedium), Kopete::UI::Global::mainWidget() ); logOff( Kopete::Account::ConnectionReset ); } void OscarAccount::slotTaskError( const Oscar::SNAC& s, int code, bool fatal ) { kDebug(OSCAR_GEN_DEBUG) << "error received from task"; kDebug(OSCAR_GEN_DEBUG) << "service: " << s.family << " subtype: " << s.subtype << " code: " << code << endl; QString message; if ( s.family == 0 && s.subtype == 0 ) { message = getFLAPErrorMessage( code ); if ( !isBusy() ) KNotification::event( QLatin1String("connection_error"), i18nc( "account has been disconnected", "Kopete: %1 disconnected", accountId() ), message, myself()->onlineStatus().protocolIcon(KIconLoader::SizeMedium), Kopete::UI::Global::mainWidget() ); switch ( code ) { case 0x0000: logOff( Kopete::Account::Unknown ); break; case 0x0004: case 0x0005: logOff( Kopete::Account::BadPassword ); break; case 0x0007: case 0x0008: case 0x0009: case 0x0011: logOff( Kopete::Account::BadUserName ); break; case 0x001B: case 0x001C: OscarVersionUpdater::self()->update( d->versionUpdaterStamp ); if ( !d->versionAlreadyUpdated ) { logOff( Kopete::Account::Unknown ); d->versionAlreadyUpdated = true; } else { logOff( Kopete::Account::Manual ); } break; default: logOff( Kopete::Account::Manual ); } return; } if ( !fatal ) message = i18n("There was an error in the protocol handling; it was not fatal, so you will not be disconnected."); else message = i18n("There was an error in the protocol handling; automatic reconnection occurring."); if ( !isBusy() ) KNotification::event( QLatin1String("server_error"), i18n("Kopete: OSCAR Protocol error"), message, myself()->onlineStatus().protocolIcon(KIconLoader::SizeMedium), Kopete::UI::Global::mainWidget() ); if ( fatal ) logOff( Kopete::Account::ConnectionReset ); } void OscarAccount::updateBuddyIconInSSI() { if ( !engine()->isActive() ) return; QString photoPath = myself()->property( Kopete::Global::Properties::self()->photo() ).value().toString(); ContactManager* ssi = engine()->ssiManager(); OContact item = ssi->findItemForIconByRef( 1 ); if ( photoPath.isEmpty() ) { if ( item ) { kDebug(OSCAR_GEN_DEBUG) << "Removing icon hash item from ssi"; OContact s(item); //remove hash and alias QList tList( item.tlvList() ); TLV t = Oscar::findTLV( tList, 0x00D5 ); if ( t ) tList.removeAll( t ); t = Oscar::findTLV( tList, 0x0131 ); if ( t ) tList.removeAll( t ); item.setTLVList( tList ); //s is old, item is new. modification will occur engine()->modifyContactItem( s, item ); } } else { QFile iconFile( photoPath ); iconFile.open( QIODevice::ReadOnly ); QCryptographicHash iconHash( QCryptographicHash::Md5 ); iconHash.addData( &iconFile ); kDebug(OSCAR_GEN_DEBUG) << "hash is :" << iconHash.result().toHex(); QByteArray iconTLVData; iconTLVData.resize( 18 ); iconTLVData[0] = ( d->engine->isIcq() ) ? 0x01 : 0x00; iconTLVData[1] = 0x10; memcpy( iconTLVData.data() + 2, iconHash.result(), 16 ); QList tList; tList.append( TLV( 0x00D5, iconTLVData.size(), iconTLVData ) ); tList.append( TLV( 0x0131, 0, 0 ) ); //find old item, create updated item if ( !item ) { kDebug(OSCAR_GEN_DEBUG) << "no existing icon hash item in ssi. creating new"; OContact s( "1", 0, ssi->nextContactId(), ROSTER_BUDDYICONS, tList ); //item is a non-valid ssi item, so the function will add an item kDebug(OSCAR_GEN_DEBUG) << "setting new icon item"; engine()->modifyContactItem( item, s ); } else { //found an item OContact s(item); if ( Oscar::updateTLVs( s, tList ) == true ) { kDebug(OSCAR_GEN_DEBUG) << "modifying old item in ssi."; //s is old, item is new. modification will occur engine()->modifyContactItem( item, s ); } else { kDebug(OSCAR_GEN_DEBUG) << "not updating, item is the same."; } } iconFile.close(); } d->buddyIconDirty = false; } void OscarAccount::slotSendBuddyIcon() { //need to disconnect because we could end up with many connections QObject::disconnect( engine(), SIGNAL(iconServerConnected()), this, SLOT(slotSendBuddyIcon()) ); QString photoPath = myself()->property( Kopete::Global::Properties::self()->photo() ).value().toString(); if ( photoPath.isEmpty() ) return; kDebug(OSCAR_RAW_DEBUG) << photoPath; QFile iconFile( photoPath ); if ( iconFile.open( QIODevice::ReadOnly ) ) { if ( !engine()->hasIconConnection() ) { //will send icon when we connect to icon server QObject::connect( engine(), SIGNAL(iconServerConnected()), this, SLOT(slotSendBuddyIcon()) ); engine()->connectToIconServer(); return; } QByteArray imageData = iconFile.readAll(); engine()->sendBuddyIcon( imageData ); } } void OscarAccount::slotIdentityPropertyChanged( Kopete::PropertyContainer*, const QString &key, const QVariant&, const QVariant &newValue ) { kDebug(OSCAR_GEN_DEBUG) << "Identity property changed"; if ( key == Kopete::Global::Properties::self()->photo().key() ) { updateBuddyIcon( newValue.toString() ); } } void OscarAccount::slotGoOffline() { } void OscarAccount::slotGoOnline() { } QString OscarAccount::getFLAPErrorMessage( int code ) { bool isICQ = d->engine->isIcq(); QString acctType = isICQ ? i18n("ICQ") : i18n("AIM"); QString acctDescription = isICQ ? i18nc("ICQ user id", "UIN") : i18nc("AIM user id", "screen name"); QString reason; //FLAP errors are always fatal //negative codes are things added by liboscar developers //to indicate generic errors in the task switch ( code ) { case 0x0001: if ( isConnected() ) // multiple logins (on same UIN) { reason = i18n( "You have logged in more than once with the same %1," \ " account %2 is now disconnected.", acctDescription, accountId() ); } else // error while logging in { reason = i18n( "Sign on failed because either your %1 or " \ "password are invalid. Please check your settings for account %2.", acctDescription, accountId() ); } break; case 0x0002: // Service temporarily unavailable case 0x0014: // Reservation map error reason = i18n("The %1 service is temporarily unavailable. Please try again later.", acctType ); break; case 0x0004: // Incorrect nick or password, re-enter case 0x0005: // Mismatch nick or password, re-enter reason = i18n("Could not sign on to %1 with account %2 because the " \ "password was incorrect.", acctType, accountId() ); break; case 0x0007: // non-existent ICQ# case 0x0008: // non-existent ICQ# reason = i18n("Could not sign on to %1 with nonexistent account %2.", acctType, accountId() ); break; case 0x0009: // Expired account reason = i18n("Sign on to %1 failed because your account %2 expired.", acctType, accountId() ); break; case 0x0011: // Suspended account reason = i18n("Sign on to %1 failed because your account %2 is " \ "currently suspended.", acctType, accountId() ); break; case 0x0015: // too many clients from same IP case 0x0016: // too many clients from same IP case 0x0017: // too many clients from same IP (reservation) reason = i18n("Could not sign on to %1 as there are too many clients" \ " from the same computer.", acctType ); break; case 0x0018: // rate exceeded (turboing) if ( isConnected() ) { reason = i18n("Account %1 was blocked on the %2 server for" \ " sending messages too quickly." \ " Wait ten minutes and try again." \ " If you continue to try, you will" \ " need to wait even longer.", accountId(), acctType ); } else { reason = i18n("Account %1 was blocked on the %2 server for" \ " reconnecting too quickly." \ " Wait ten minutes and try again." \ " If you continue to try, you will" \ " need to wait even longer.", accountId(), acctType) ; } break; case 0x001B: case 0x001C: if ( !d->versionAlreadyUpdated ) { reason = i18n("Sign on to %1 with your account %2 failed.", acctType, accountId() ); } else { reason = i18n( "The %1 server thinks the client you are using is " \ - "too old. Please report this as a bug at http://bugs.kde.org", + "too old. Please report this as a bug at https://bugs.kde.org", acctType ); } break; case 0x0022: // Account suspended because of your age (age < 13) reason = i18n("Account %1 was disabled on the %2 server because " \ "of your age (under than 13).", accountId(), acctType ); break; default: if ( !isConnected() ) { reason = i18n("Sign on to %1 with your account %2 failed.", acctType, accountId() ); } break; } return reason; } QString OscarAccount::makeWellFormedXML( const QString& message ) const { // QList>, if tagName isn't empty then data is // otherwise data is normal text which is between tags. QList< QPair > tagsAndText; QRegExp tagRegExp( QString::fromLatin1("<([/]?[\\w]+).*>") ); tagRegExp.setMinimal( true ); int index = 0; while ( tagRegExp.indexIn( message, index ) != -1 ) { if ( index < tagRegExp.pos() ) { // Append text which was between tags QPair pair; pair.second = message.mid( index, tagRegExp.pos() - index ); tagsAndText.append( pair ); } // Add tag QPair pair; pair.first = tagRegExp.cap( 1 ); pair.second = message.mid( tagRegExp.pos(), tagRegExp.matchedLength() ); tagsAndText.append( pair ); index = tagRegExp.pos() + tagRegExp.matchedLength(); } if ( index < message.length() ) { // Add text which was at end QPair pair; pair.second = message.mid( index, message.length() - index ); tagsAndText.append( pair ); } // Make the list well-formed QStack openTags; const int tagsAndTextCount = tagsAndText.count(); for ( int i = 0; i < tagsAndTextCount; ++i ) { QPair pair = tagsAndText.at( i ); if ( pair.first.isEmpty() ) // We don't move text continue; bool endTag = pair.first.startsWith( "/" ); if ( !endTag ) { openTags.push( pair.first ); } else if ( !openTags.isEmpty() ) { QString desiredTag = "/" + openTags.pop(); if ( pair.first != desiredTag ) { // Find desired end tag and insert it into correct position for ( int j = i + 1; j < tagsAndTextCount; ++j ) { QPair pair2 = tagsAndText.at( j ); if ( pair2.first.isEmpty() ) { // Text is between desired tag so we can't move it qWarning() << "Can't make well-formed XML!"; return message; } if ( pair2.first == desiredTag ) { // Move tag to correct position tagsAndText.removeAt( j ); tagsAndText.insert( i, pair2 ); break; } } } } } QString wellFormedMessage; for ( int i = 0; i < tagsAndTextCount; ++i ) wellFormedMessage += tagsAndText.at( i ).second; return wellFormedMessage; } QString OscarAccount::addQuotesAroundAttributes( QString message ) const { int sIndex = 0; int eIndex = 0; int searchIndex = 0; QRegExp attrRegExp( "[\\d\\w]*=[^\"'/>\\s]+" ); QString attrValue( "\"%1\"" ); sIndex = message.indexOf( "<", eIndex ); eIndex = message.indexOf( ">", sIndex ); if ( sIndex == -1 || eIndex == -1 ) return message; while ( attrRegExp.indexIn( message, searchIndex ) != -1 ) { int startReplace = message.indexOf( "=", attrRegExp.pos() ) + 1; int replaceLength = attrRegExp.pos() + attrRegExp.matchedLength() - startReplace; while ( eIndex != -1 && sIndex != -1 && startReplace + replaceLength > eIndex ) { sIndex = message.indexOf( "<", eIndex ); eIndex = message.indexOf( ">", sIndex ); } if ( sIndex == -1 || eIndex == -1 ) return message; searchIndex = attrRegExp.pos() + attrRegExp.matchedLength(); if ( startReplace <= sIndex ) continue; QString replaceText = attrValue.arg( message.mid( startReplace, replaceLength ) ); message.replace( startReplace, replaceLength, replaceText ); searchIndex += 2; eIndex += 2; } return message; } QString OscarAccount::sanitizedPlainMessage( const QString& message ) const { // FIXME: messages from AIM to ICQ shouldn't be escaped, we need to redesign this QString sanitizedMsg = (d->engine->isIcq()) ? message.toHtmlEscaped() : message; sanitizedMsg.replace( QRegExp(QString::fromLatin1("[\r]?[\n]")), QString::fromLatin1("
") ); return sanitizedMsg; } QList OscarAccount::getElementsByTagNameCI( const QDomNode& node, const QString& tagName ) const { QList nodeList; QDomNode childNode = node.firstChild(); while ( !childNode.isNull() ) { nodeList.append( getElementsByTagNameCI( childNode, tagName ) ); if ( childNode.isElement() && childNode.nodeName().compare( tagName, Qt::CaseInsensitive ) == 0 ) nodeList.append( childNode ); childNode = childNode.nextSibling(); } return nodeList; } void OscarAccount::createClientStream( ClientStream **clientStream ) { QSslSocket* tcpSocket = new QSslSocket(); if (configGroup()->readEntry( QString::fromLatin1( "ProxyEnable" ), false)) { QString proxyExp=configGroup()->readEntry( QString::fromLatin1( "ProxyServer" ), QString() ); int proxyPort=configGroup()->readEntry( QString::fromLatin1( "ProxyPort" ), 0 ); bool proxySocks5=configGroup()->readEntry( QString::fromLatin1( "ProxySocks5" ), false ); tcpSocket->setProxy(QNetworkProxy(proxySocks5 ? QNetworkProxy::Socks5Proxy : QNetworkProxy::HttpProxy, proxyExp, proxyPort)); } else { const QString &proxyUrl = KProtocolManager::proxyForUrl( KUrl( "http:" ) ); if (!proxyUrl.isEmpty() && proxyUrl != QLatin1String( "DIRECT" )) { const KUrl url( proxyUrl ); QNetworkProxy::ProxyType proxyType = QNetworkProxy::NoProxy; if (url.protocol() == QLatin1String( "http" )) proxyType = QNetworkProxy::HttpProxy; else if (url.protocol() == QLatin1String( "socks" )) proxyType = QNetworkProxy::Socks5Proxy; if (proxyType != QNetworkProxy::NoProxy) tcpSocket->setProxy( QNetworkProxy( proxyType, url.host(), url.port(), url.user(), url.pass() ) ); } } ClientStream *cs = new ClientStream( tcpSocket, 0 ); Kopete::SocketTimeoutWatcher* timeoutWatcher = Kopete::SocketTimeoutWatcher::watch(tcpSocket); if ( timeoutWatcher ) { QObject::connect( timeoutWatcher, SIGNAL(error(QAbstractSocket::SocketError)), cs, SLOT(socketError(QAbstractSocket::SocketError)) ); } *clientStream = cs; }