/* Copyright (c) 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Diagnostics; using System.IO; using System.Text; using System.Windows.Forms; using System.Threading; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Ipc; using Microsoft.Win32; using Google.GData.Documents; using Google.GData.Client; namespace DocListUploader { /// /// The main form of the uploader, controls most of the service interaction. /// It is not actually displayed to the user. /// public partial class HiddenForm : Form { //A child form for the user to interact with. private OptionsForm optionsForm; //Keeps track of our logged in state. public bool loggedIn = false; //A connection with the DocList API. private DocumentsService service; //The name of the shell context menu option. private const string KEY_NAME = "Send to Google Docs"; //Keeps track of if we've minimized the OptionsForm before. private bool firstMinimize = true; //The timer in milliseconds to display balloon tips. private const int BALLOON_TIMER = 10000; //The most recently uploaded document. private DocumentEntry lastUploadEntry = null; //Keeps track of if the last balloon tip was an upload complete message. private bool lastToolTipWasUpload = false; /// /// Constructor for the form. /// public HiddenForm() { InitializeComponent(); optionsForm = new OptionsForm(this); ListenForSuccessor(); } /// /// The main entry point for the application. /// [STAThread] static void Main(string[] args) { bool firstInstance; Mutex mutex = new Mutex(true, "Local\\DocListUploader", out firstInstance); if (!firstInstance) { //If we have an argument, we were most likely called by //the shell context menu if (args.Length == 1) { NotifyPredecessor(args[0]); } return; } Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new HiddenForm()); } /// /// This is run by the first instance of the program in order /// to accept information from later instances using the /// .NET Framework's IPC mechanism. /// public void ListenForSuccessor() { IpcServerChannel serverChannel = new IpcServerChannel("DocListUploader"); ChannelServices.RegisterChannel(serverChannel, false); RemoteMessage remoteMessage = new RemoteMessage(this); RemotingServices.Marshal(remoteMessage,"FirstInstance"); } /// /// This is used by later instances of the program when /// executed from the shell's context menu to pass along /// the path of a file that will be uploaded. This message /// is passed using the .NET Framework's IPC mechanism. /// /// The file to be uploaded public static void NotifyPredecessor(string file) { IpcClientChannel clientChannel = new IpcClientChannel(); ChannelServices.RegisterChannel(clientChannel, false); RemoteMessage message = (RemoteMessage) Activator.GetObject(typeof(RemoteMessage), "ipc://DocListUploader/FirstInstance"); if (!message.Equals(null)) { message.SendMessage(file); } } /// /// Authenticates to Google servers /// /// The user's username (e-mail) /// The user's password /// Thrown on invalid credentials. public void Login(string username, string password) { if(loggedIn) { throw new ApplicationException("Already logged in."); } try { service = new DocumentsService("DocListUploader"); ((GDataRequestFactory) service.RequestFactory).KeepAlive = false; service.setUserCredentials(username, password); //force the service to authenticate DocumentsListQuery query = new DocumentsListQuery(); query.NumberToRetrieve = 1; service.Query(query); loggedIn = true; } catch(AuthenticationException e) { loggedIn = false; service = null; throw e; } } /// /// Logs the user out of Google Docs. /// public void Logout() { loggedIn = false; service = null; } /// /// Retrieves a list of documents from the server. /// /// The list of documents as a DocumentsFeed. public DocumentsFeed GetDocs() { DocumentsListQuery query = new DocumentsListQuery(); DocumentsFeed feed = service.Query(query); return feed; } /// /// Some error handling around the file upload method. Used for handling files from the context menu. /// /// The file to upload. public void HandleUpload(string file) { if (!loggedIn) { MessageBox.Show("Please log in before uploading documents", "Upload Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } try { DocListNotifyIcon.ShowBalloonTip(BALLOON_TIMER, "Uploading", "Uploading " + file, ToolTipIcon.Info); lastToolTipWasUpload = false; UploadFile(file); DocListNotifyIcon.ShowBalloonTip(BALLOON_TIMER, "Upload Success", "Successfully uploaded " + file, ToolTipIcon.Info); lastToolTipWasUpload = true; optionsForm.UpdateDocList(); } catch (ArgumentException) { DocListNotifyIcon.ShowBalloonTip(BALLOON_TIMER, "Upload Error", "Error: unable to upload the file: '" + file + "'. It is not one of the valid types.", ToolTipIcon.Error); lastToolTipWasUpload = false; } catch (Exception ex) { DocListNotifyIcon.ShowBalloonTip(BALLOON_TIMER, "Upload Error", "Error: unable to upload the file: '" + file + "'. " + ex.Message, ToolTipIcon.Error); lastToolTipWasUpload = false; } } /// /// Uploads the file to Google Docs /// /// The file with path to upload /// Thrown when user isn't logged in. public void UploadFile(string fileName) { if (!loggedIn) { throw new ApplicationException("Need to be logged in to upload documents."); } else { lastUploadEntry = service.UploadDocument(fileName, null); } } /// /// Called when the OptionsForm is minimized/hidden. /// public void HandleMinimize() { if (firstMinimize) { DocListNotifyIcon.ShowBalloonTip(BALLOON_TIMER, "DocList Uploader", "Uploader now running in the system tray.", ToolTipIcon.Info); lastToolTipWasUpload = false; firstMinimize = false; } } /// /// Adds the application to the shell's context menu by /// creating a registry key. /// public void Register() { RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\\Classes\\*\\shell\\" + KEY_NAME + "\\command"); if (key == null) { key = Registry.CurrentUser.CreateSubKey("Software\\Classes\\*\\shell\\" + KEY_NAME + "\\command"); } key.SetValue("", Application.ExecutablePath + " \"%1\""); } /// /// Removes the application from the shell's context menu /// by deleting a registry key. /// public void UnRegister() { RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\\Classes\\*\\shell\\" + KEY_NAME); if (key != null) { Registry.CurrentUser.DeleteSubKeyTree("Software\\Classes\\*\\shell\\" + KEY_NAME); } } /// /// Displays the OptionsForm. /// private void ShowOptionsWindow() { optionsForm.Show(); if (optionsForm.WindowState == FormWindowState.Minimized) { optionsForm.WindowState = FormWindowState.Normal; } } private void DocListNotifyIcon_DoubleClick(object sender, EventArgs e) { optionsForm.Show(); } private void CloseMenuItem_Click(object sender, EventArgs e) { Close(); } private void HiddenForm_Load(object sender, EventArgs e) { Hide(); ShowOptionsWindow(); } private void optionsToolStripMenuItem_Click(object sender, EventArgs e) { ShowOptionsWindow(); } private void HiddenForm_FormClosing(object sender, FormClosingEventArgs e) { UnRegister(); } private void DocListNotifyIcon_MouseClick(object sender, MouseEventArgs e) { //A single left click should be the same as a double-click. if (e.Button == MouseButtons.Left) { ShowOptionsWindow(); } } private void DocListNotifyIcon_BalloonTipClicked(object sender, EventArgs e) { if (lastUploadEntry != null && lastToolTipWasUpload) { //display the last uploaded document in the browser. try { Process.Start(lastUploadEntry.AlternateUri.ToString()); } catch (Win32Exception) { //nothing is registered to handle URLs, so let's use IE! Process.Start("IExplore.exe", lastUploadEntry.AlternateUri.ToString()); } } } } }