<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
   <channel>
      <title>Photon Development</title>
      <link>http://www.photontech.org/dev/</link>
      <description></description>
      <language>en</language>
      <copyright>Copyright 2009</copyright>
      <lastBuildDate>Sat, 09 Jun 2007 18:24:35 -0500</lastBuildDate>
      <generator>http://www.sixapart.com/movabletype/</generator>
      <docs>http://blogs.law.harvard.edu/tech/rss</docs> 

      
      <item>
         <title>WhiteRook: NSA</title>
         <description><![CDATA[By Brandon W. Yuille

I'd like to introduce the new WhiteRook: NSA. Not only has the interface been updated, there will also be many new features included such as: shredding files, file recovery, and file encryption.

<h3>The New Interface</h3>
<img src="http://www.photontech.org/images/whiterook/wr_screen1.jpg" />]]></description>
         <link>http://www.photontech.org/dev/2007/06/whiterook_nsa.html</link>
         <guid>http://www.photontech.org/dev/2007/06/whiterook_nsa.html</guid>
        
          <category domain="http://www.sixapart.com/ns/types#category">WhiteRook</category>
        
        
          <category domain="http://www.sixapart.com/ns/types#tag">EntryMore</category>
        
         <pubDate>Sat, 09 Jun 2007 18:24:35 -0500</pubDate>
      </item>
      
      <item>
         <title>Follow Up: Google Pushes Privacy Out The 6th Floor Window</title>
         <description><![CDATA[By Brandon W. Yuille

This article reminded me of why I did not go work for Google: <a href="http://www.ft.com/cms/s/c3e49548-088e-11dc-b11e-000b5df10621.html" target="_blank">Google’s goal to organise your daily life</a>.]]></description>
         <link>http://www.photontech.org/dev/2007/05/follow_up_google_pushes_privac.html</link>
         <guid>http://www.photontech.org/dev/2007/05/follow_up_google_pushes_privac.html</guid>
        
          <category domain="http://www.sixapart.com/ns/types#category">Other</category>
        
        
         <pubDate>Wed, 23 May 2007 21:19:22 -0500</pubDate>
      </item>
      
      <item>
         <title>General Development Update</title>
         <description><![CDATA[By Brandon W. Yuille

I would like to give everyone a quick update about what has been going on around here (major development).

Here's a quick break down of all the Photon products:


<h3>Call Capture</h3>We have a lot of great ideas about the future of this product, yet not enough time to implement them all before the official release (no date is set for now). I can tell you, with the new servers installed in a high bandwidth location things are looking great. We're still working on some compatibility issues with Vista, but those should be cleared up soon.

At the moment we're working on implementing as many reporting features as possible (some of them are going to be unbelievable). Once completed we will implement the new advanced search engine to help you easily search through contacts, tasks, documents, and Call Capture extensions.


<h3>Double Slit Application Installer</h3>We're still working on this one as this is the installer used by all of our products. The DS Project Builder is currently on its way to being available for beta testers. Get ready for some advanced installation techniques!


<h3>WhiteRook Privacy Guard</h3>Development for WhiteRook is moving slowly while Call Capture is still priority. We have begun work on performing advanced shredding and encryption operations yet neither are at a release point. I'll keep you updated.]]></description>
         <link>http://www.photontech.org/dev/2007/03/general_development_update.html</link>
         <guid>http://www.photontech.org/dev/2007/03/general_development_update.html</guid>
        
          <category domain="http://www.sixapart.com/ns/types#category">Other</category>
        
        
          <category domain="http://www.sixapart.com/ns/types#tag">EntryMore</category>
        
         <pubDate>Thu, 29 Mar 2007 05:07:37 -0500</pubDate>
      </item>
      
      <item>
         <title>Call Capture Servers Up and Ready!</title>
         <description><![CDATA[By Brandon W. Yuille

Just wanted to post some pictures of the Call Capture servers that took us all night to install and get properly configured:

<img src="http://www.photontech.org/images/equipment/0323070729-01.jpg" /><img src="http://www.photontech.org/images/equipment/0323070732-00.jpg" />]]></description>
         <link>http://www.photontech.org/dev/2007/03/call_capture_servers_up_and_re.html</link>
         <guid>http://www.photontech.org/dev/2007/03/call_capture_servers_up_and_re.html</guid>
        
          <category domain="http://www.sixapart.com/ns/types#category">Call Capture</category>
        
        
         <pubDate>Fri, 23 Mar 2007 18:18:52 -0500</pubDate>
      </item>
      
      <item>
         <title>Google Moves to Protect Privacy, Yet Not Far Enough</title>
         <description><![CDATA[By Brandon W. Yuille

I recently read this post by Peter Fleischer on Google's blog, informing of new privacy policies to protect their users:

<h3>The Privacy Post From Google</h3>"When you search on Google, we collect information about your search, such as the query itself, IP addresses and cookie details. Previously, we kept this data for as long as it was useful. Today we're pleased to report a change in our privacy policy: Unless we're legally required to retain log data for longer, we will anonymize our server logs after a limited period of time. When we implement this policy change in the coming months, we will continue to keep server log data (so that we can improve Google's services and protect them from security and other abuses)—but will make this data much more anonymous, so that it can no longer be identified with individual users, after 18-24 months.

Just as we continuously work to improve our products, we also work toward having the best privacy practices for our users. This includes designing privacy protections into our products (like Google Talk's “off the record” feature or Google Desktop’s “pause” and “lock search” controls). This also means providing clear, easy to understand privacy policies that help you make informed decisions about using our services.

After talking with leading privacy stakeholders in Europe and the U.S., we're pleased to be taking this important step toward protecting your privacy. By anonymizing our server logs after 18-24 months, we think we’re striking the right balance between two goals: continuing to improve Google’s services for you, while providing more transparency and certainty about our retention practices. In the future, it's possible that data retention laws will obligate us to retain logs for longer periods. Of course, you can always choose to have us retain this data for more personalized services like Search History. But that's up to you.

Our engineers are already busy working out the technical details, and we hope to implement this new data policy over the coming months (and within a year's time). We’ll communicate more as we work out these details, but for now, we wanted you to know that we’re working on this additional step to strengthen your privacy."
<a href="http://googleblog.blogspot.com/2007/03/taking-steps-to-further-improve-our.html" target="_blank">http://googleblog.blogspot.com/2007/03/taking-steps-to-further-improve-our.html</a>


<h3>My Thoughts</h3>After reading this I realized that there was more that needed to be said to Google, yet from what I can tell no way to say it. Google needs to understand that because they are the largest search engine on the internet the US government is surly devising ways to monitor its use.

As we have already seen in the telecom and ISP industry, the FCC has ruled that CALEA (Communications Assistance for Law Enforcement Act) must be adopted by every phone and internet provider. They have extended the hunt for terrorism to our own vehicles, places of work, and homes.

The interest and curiosity of Americans for free information is now being closely watched and monitored. This poses an extremely large threat as the Patriot Act has removed the right to not be simply labeled a terrorist and taken from your daily freedoms.

The Bells or SBC or AT&T as they are now called are in very much cooperation with the government in this anti-freedom of information push. From their angle, the incentive is to please and in return the FCC will allow them to regulate the content that is transferred over the internet. If AT&T is able to charge whatever they feel reasonable for say VoIP traffic there will be no competitors left to attack this monopoly.

Google is the last hope to keep the internet free. There have been rumors of <a href="http://www.searchenginejournal.com/index.php?p=2239" target="_blank">Google creating their own internet</a> to compete against AT&T, as Google has been giving them very large amounts of money to support their high bandwidth needs.

For Google to fight for a free internet, I feel it is imperative that they first start by making their search engine and other tools completely anonymous. Google's current attempt at making "this data much more anonymous" by only holding this data for "18-24 months" is not enough, as it will still drive government subpoenas for release of user data and search habits.

Freedom of information is fading (among others) and all of our hopes are in the hands of Google.]]></description>
         <link>http://www.photontech.org/dev/2007/03/google_moves_to_protect_privac.html</link>
         <guid>http://www.photontech.org/dev/2007/03/google_moves_to_protect_privac.html</guid>
        
          <category domain="http://www.sixapart.com/ns/types#category">Other</category>
        
        
          <category domain="http://www.sixapart.com/ns/types#tag">EntryMore</category>
        
         <pubDate>Fri, 16 Mar 2007 00:00:45 -0500</pubDate>
      </item>
      
      <item>
         <title>New Call Capture Video</title>
         <description><![CDATA[By Brandon W. Yuille

This is a new video produced by us, to promote <a href="/products/callcapture/">Call Capture</a>.

Enjoy :)

<script type="text/javascript" src="/products/callcapture/ufo.js"></script>
      <div id="player1" align="center"><a href="http://www.macromedia.com/go/getflashplayer">Get the Flash Player</a> to see this player.</div>
	  <script type="text/javascript">var FO = { movie:"/products/callcapture/flvplayer.swf",width:"470",height:"330",majorversion:"7",build:"0",bgcolor:"#000000",allowfullscreen:"true", flashvars:"file=/products/callcapture/Main.flv" };UFO.create(FO, "player1");</script>]]></description>
         <link>http://www.photontech.org/dev/2007/03/new_call_capture_video.html</link>
         <guid>http://www.photontech.org/dev/2007/03/new_call_capture_video.html</guid>
        
          <category domain="http://www.sixapart.com/ns/types#category">Call Capture</category>
        
        
         <pubDate>Wed, 14 Mar 2007 13:38:34 -0500</pubDate>
      </item>
      
      <item>
         <title>Outlook 2007 Password Prompt and SQL Server 2005 Unable to Install</title>
         <description><![CDATA[By Brandon W. Yuille

Whether you are constantly being annoyed by Outlook 2007's password dialog for you to enter your password or you simply cannot install SQL Server 2005, the problem is actually one in the same.

Outlook 2007 symptoms are that it simply will not save your password no matter how many times you check “Remember Password”. This problem alone almost made me uninstall Office 2007 and get my money back. Imagine you are working hard on something and a dialog box pops up to steal your input right as you are in the middle of a sentence or line of code… Yes excruciating pain indeed.

SQL Server 2005 seems to have a bigger problem, not quite as annoying though. When attempting to install, you get a message box right away alerting you to the fact that the installation has failed and there is a log of the install and what went wrong in: "%Program Files%\Microsoft SQL Server\90\Setup Bootstrap\LOG\Summary.txt". The Summary.txt file won’t tell you much, but if you move into the Files directory you will see a detailed log. Once open, the error you see looks like the following:

<pre>…
Error: Action "ParseBootstrapOptionsAction" failed during execution.  Error information reported during run:
Could not parse command line due to datastore exception.
  Source File Name: utillib\persisthelpers.cpp
Compiler Timestamp: Fri Jul 29 01:13:55 2005
     Function Name: writeEncryptedString
Source Line Number: 124
…</pre>

It turns out the answer to all this pain and suffering is by adding a simple registry key: <strong>"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders\AppData"</strong>. AppData is of type REG_EXPAND_SZ and should contain the value: <strong>"%userprofile%\Application Data"</strong>.

AppData was for some reason non-existent on my computer, so if you are having problems like those above please check to make sure you have this registry key. If you don’t know how to add a registry key, make a post here and I’ll update you.

Good luck!]]></description>
         <link>http://www.photontech.org/dev/2007/02/outlook_2007_password_prompt_a.html</link>
         <guid>http://www.photontech.org/dev/2007/02/outlook_2007_password_prompt_a.html</guid>
        
          <category domain="http://www.sixapart.com/ns/types#category">Other</category>
        
        
          <category domain="http://www.sixapart.com/ns/types#tag">EntryMore</category>
        
         <pubDate>Mon, 26 Feb 2007 09:45:59 -0500</pubDate>
      </item>
      
      <item>
         <title>Redirecting Console Input/Output using Pipes</title>
         <description><![CDATA[By Brandon W. Yuille

I wanted to create my own shell or command prompt, which I could customize (make transparent, ect). I started by using Pipes to accomplish this. Well, I ran into some road blocks when I tried to redirect applications like telnet.exe. The problem lies in the fact that telnet checks to make sure that the std handles are not being redirected via pipes. That's where I decided to give up on my little project (the main reason I started was to use my console for telnet).

With all the information I gained from attempting this, I figure may as well share it. Also, someone might be able to figure out how to fix this problem along the way. If so, please let me know!

The code provided is a basic class (CCmdWnd) that is derived from a CWnd. I would say that none of this code is anywhere near production code. But with some work, you could make it into something nice! When looking at this code, don't worry too much about how I draw my output or anything like that. The important part is how I redirect the console IO. So pay attention to: <strong>ExecuteExternal()</strong>, <strong>ReadAndHandleOutput()</strong>, and <strong>WriteToChild()</strong>.

<h3>Class Declaration</h3><pre>// CmdWnd.h
#pragma once
// CCmdWnd

#define UWM_CONSOLEOUTPUT (WM_APP + 1)
#define UWM_CHILDEXIT (WM_APP + 2)

class CCmdWnd : public CWnd
{
	DECLARE_DYNAMIC(CCmdWnd)

public:
	CCmdWnd();
	virtual ~CCmdWnd();

protected:
	afx_msg void OnPaint();
	afx_msg BOOL OnEraseBkgnd(CDC *pDC);
	afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
	afx_msg void OnDestroy();
	afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
	afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
	afx_msg UINT OnGetDlgCode();
	afx_msg LRESULT OnConsoleOutput(WPARAM wParam, LPARAM lParam);
	afx_msg LRESULT OnChildExit(WPARAM wParam, LPARAM lParam);
	DECLARE_MESSAGE_MAP()

	BOOL ExecuteInternalFile(CString ExeName, CString &Response);
	BOOL ExecuteExternal(CString FileName);

	int GetNewLineCount(CString text);

	BOOL GetPrevCommand(CString &command){
		if((m_commandindex-1) >= 0){
			m_commandindex--; command = m_vcommands.at(m_commandindex);
			return TRUE;}
		return FALSE;};
	BOOL GetNextCommand(CString &command){
		if((m_commandindex+1) < m_vcommands.size()){
			m_commandindex++; command = m_vcommands.at(m_commandindex);
			return TRUE;}
		return FALSE;};
	void AddCommand(CString command){
		m_vcommands.push_back(command);
		m_commandindex = m_vcommands.size();
	};

	std::vector<CString> m_vcommands;
	int m_commandindex;
	CString m_curcommand;
	CString m_output;
	CFont m_font;

	static UINT ReadAndHandleOutput(LPVOID pParam);

	BOOL WriteToChild(CString text);
	HANDLE m_hChildProcess;
	HANDLE m_hPipeRead;
	HANDLE m_hPipeWrite;
};</pre>

<h3>Class Implementation</h3><pre>// CmdWnd.cpp : implementation file
//

#include "stdafx.h"
#include "CmdWnd.h"


// CCmdWnd

IMPLEMENT_DYNAMIC(CCmdWnd, CWnd)

CCmdWnd::CCmdWnd()
{
	NONCLIENTMETRICS ncm;
	ncm.cbSize = sizeof(NONCLIENTMETRICS);
	SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
	strcpy(ncm.lfCaptionFont.lfFaceName, "Calibri");
	m_font.CreateFontIndirect(&ncm.lfCaptionFont);

	m_commandindex = 0;
	m_hChildProcess = NULL;
}

CCmdWnd::~CCmdWnd()
{
}


BEGIN_MESSAGE_MAP(CCmdWnd, CWnd)
	ON_WM_CREATE()
	ON_WM_PAINT()
	ON_WM_ERASEBKGND()
	ON_WM_DESTROY()
	ON_WM_CHAR()
	ON_WM_KEYDOWN()
	ON_WM_GETDLGCODE()
	ON_MESSAGE(UWM_CONSOLEOUTPUT, OnConsoleOutput)
	ON_MESSAGE(UWM_CHILDEXIT, OnChildExit)
END_MESSAGE_MAP()



// CCmdWnd message handlers
UINT CCmdWnd::OnGetDlgCode()
{
	return DLGC_WANTTAB | DLGC_WANTARROWS | DLGC_WANTALLKEYS;
}

int CCmdWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if(CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;

	return 0;
}

void CCmdWnd::OnDestroy()
{
	CloseHandle(m_hChildProcess);
	m_hChildProcess = NULL;

	CWnd::OnDestroy();
}

BOOL CCmdWnd::OnEraseBkgnd(CDC *pDC)
{
	return TRUE;
}

void CCmdWnd::OnPaint()
{
	CPaintDC dc(this);

	CString output;
	CRect rWnd;
	CRect rOutput;
	GetClientRect(rWnd);
	CDC bufDC;
	CBitmap bufBmp;

	bufDC.CreateCompatibleDC(&dc);
	bufBmp.CreateCompatibleBitmap(&dc, rWnd.Width(), rWnd.Height());
	CBitmap *pOldBmp = bufDC.SelectObject(&bufBmp);

	bufDC.FillSolidRect(rWnd, RGB(0, 0, 0));

	bufDC.SetBkMode(TRANSPARENT);
	bufDC.SetTextColor(RGB(0, 0xFF, 0));
	CFont *pOldFont = bufDC.SelectObject(&m_font);

	// Draw all the output
	rOutput.left = rWnd.left;
	rOutput.right = rWnd.right;
	rOutput.top = 0;
	rOutput.bottom = 0;

	output = m_output;
	int lHeight = bufDC.GetTextExtent(output).cy;
	rOutput.bottom = lHeight;
	CString tmp;
	for(int i = 0; i < output.GetLength(); i++)
	{
		tmp += output.GetAt(i);
		CRect rTmp(0,0,0,0);
		bufDC.DrawText(tmp, rTmp, DT_SINGLELINE | DT_CALCRECT);
		if(rTmp.right > rWnd.right || tmp.Right(1) == "\n")
		{
			CString hold; hold = "";
			if(tmp.Right(1) == "\n")
			{
				tmp.Replace("\n", "");
				tmp.Replace("\r", "");
			}
			else
			{
				hold = tmp.Right(1);
				tmp = tmp.Left(tmp.GetLength()-1);
			}

			tmp.Replace("\n", "");
			tmp.Replace("\r", "");

			bufDC.DrawText(tmp, rOutput, DT_SINGLELINE);
			rOutput.top += lHeight; rOutput.bottom += lHeight;
			tmp = hold;
		}
	}
	tmp.Replace("\n", "");
	tmp.Replace("\r", "");
	bufDC.DrawText(tmp, rOutput, DT_SINGLELINE);
	rOutput.top += lHeight; rOutput.bottom += lHeight;

	if(m_hChildProcess == NULL)
	{
		// Draw the current command line
		rOutput.left = rWnd.left;
		rOutput.right = rWnd.right;

		CHAR szPath[MAX_PATH]; //MAX_PATH is defined as 260
		INT iLen = 0; 
		iLen = GetCurrentDirectory(MAX_PATH, szPath);
		strcat(szPath, ">");

		output = szPath;
		output += m_curcommand;
		if(rOutput.bottom == rOutput.top)
		{
			bufDC.DrawText(output, rOutput, DT_SINGLELINE | DT_CALCRECT);
			if(lHeight == 0)
				lHeight = rOutput.bottom;
		}
		tmp = "";
		for(int i = 0; i < output.GetLength(); i++)
		{
			tmp += output.GetAt(i);
			CRect rTmp(0,0,0,0);
			bufDC.DrawText(tmp, rTmp, DT_SINGLELINE | DT_CALCRECT);
			if(rTmp.right > rWnd.right)
			{
				CString hold;
				hold = tmp.Right(1);
				tmp = tmp.Left(tmp.GetLength()-1);
				bufDC.DrawText(tmp, rOutput, DT_SINGLELINE);
				rOutput.top += lHeight; rOutput.bottom += lHeight;
				tmp = hold;
			}
		}
		bufDC.DrawText(tmp, rOutput, DT_SINGLELINE);
	}

	dc.BitBlt(0, 0, rWnd.Width(), rWnd.Height(), &bufDC, 0, 0, SRCCOPY);

	bufDC.SelectObject(pOldFont);
	bufDC.SelectObject(pOldBmp);
}

void CCmdWnd::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if(m_hChildProcess != NULL)
	{
		CString tmp;
		tmp.Format("%c", nChar);
		WriteToChild(tmp);
	}
	else if(nChar >= 32)
	{
		CString tmp;
		tmp.Format("%c", nChar);
		m_curcommand += tmp;
		Invalidate();
	}
	else if(nChar == VK_BACK)
	{
		m_curcommand = m_curcommand.Left(m_curcommand.GetLength()-1);
		Invalidate();
	}
	else
		CWnd::OnChar(nChar, nRepCnt, nFlags);
}

void CCmdWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if(m_hChildProcess == NULL)
	{
		if(nChar == VK_RETURN)
		{
			CString output;
			CHAR szPath[MAX_PATH]; //MAX_PATH is defined as 260
			INT iLen = 0;
			iLen = GetCurrentDirectory(MAX_PATH, szPath);
			strcat(szPath, ">");

			output = szPath;
			output += m_curcommand;
			if(m_output != "")
				m_output += "\r\n";
			m_output += output + "\r\n";
			
			CString tmpout;
			if(m_curcommand == "")
			{
			}
			else if(ExecuteInternalFile(m_curcommand, tmpout))
			{
				AddCommand(m_curcommand);
				if(tmpout != "")
					m_output += tmpout;
			}
			else if(ExecuteExternal(m_curcommand))
			{
				AddCommand(m_curcommand);
				if(tmpout != "")
					m_output += tmpout;
			}
			else
			{
				AddCommand(m_curcommand);
				m_output += m_curcommand + "' is not recognized as an internal or external command.";
			}

			m_curcommand = "";

			Invalidate();
		}
		else if(nChar == VK_UP)
		{
			GetPrevCommand(m_curcommand);
			Invalidate();
		}
		else if(nChar == VK_DOWN)
		{
			GetNextCommand(m_curcommand);
			Invalidate();
		}
	}
	else // The child process is running
	{
		if(nChar == VK_RETURN)
		{
			WriteToChild("\r\n");
		}
	}
}

BOOL CCmdWnd::ExecuteInternalFile(CString ExeName, CString &Response)
{
	if(ExeName == "cls")
	{
		m_output = "";
		return TRUE;
	}
	else if(ExeName == "exit")
	{
		GetParent()->PostMessage(WM_CLOSE);
		return TRUE;
	}

	return FALSE;
}

int CCmdWnd::GetNewLineCount(CString text)
{
	int icount = 0;
	for(int i = 0; i < text.GetLength(); i++)
	{
		if(text.GetAt(i) == '\n')
			icount++;
	}
	return icount;
}

BOOL CCmdWnd::ExecuteExternal(CString FileName)
{
	HANDLE hOutputReadTmp, hOutputWrite;
	HANDLE hInputWriteTmp, hInputRead;
	HANDLE hErrorWrite;
	HANDLE hThread;
	DWORD ThreadId;
	SECURITY_ATTRIBUTES sa;

	if(FileName.GetLength() > 1023)
		return FALSE;

	char command[1024];
	strcpy(command, FileName.GetBuffer(FileName.GetLength()));

	m_hChildProcess = NULL;

	// Set up the security attributes struct.
	sa.nLength= sizeof(SECURITY_ATTRIBUTES);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = TRUE;

	// Create the child output pipe.
	if(!CreatePipe(&hOutputReadTmp, &hOutputWrite, &sa, 0))
	{
		TRACE0("CreatePipe Failed!\r\n");
	}

	// Create a duplicate of the output write handle for the std error
	// write handle. This is necessary in case the child application
	// closes one of its std output handles.
	if (!DuplicateHandle(GetCurrentProcess(),hOutputWrite,
					   GetCurrentProcess(),&hErrorWrite,0,
					   TRUE,DUPLICATE_SAME_ACCESS))
	{
		TRACE0("DuplicateHandle Failed!\r\n");
	}


	// Create the child input pipe.
	if (!CreatePipe(&hInputRead,&hInputWriteTmp,&sa,0))
	{
	}

	if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp,
					   GetCurrentProcess(),
					   &m_hPipeRead, // Address of new handle.
					   0,FALSE, // Make it uninheritable.
					   DUPLICATE_SAME_ACCESS))
	{
	}

	if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp,
					   GetCurrentProcess(),
					   &m_hPipeWrite, // Address of new handle.
					   0,FALSE, // Make it uninheritable.
					   DUPLICATE_SAME_ACCESS))
	{
	}

	if(!CloseHandle(hOutputReadTmp))
	{
	}
	if(!CloseHandle(hInputWriteTmp))
	{
	}

	PROCESS_INFORMATION pi;
	STARTUPINFO si;

	// Set up the start up info struct.
	ZeroMemory(&si,sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);
	si.dwFlags = STARTF_USESTDHANDLES;
	si.hStdOutput = hOutputWrite;
	si.hStdInput  = hInputRead;
	si.hStdError  = hErrorWrite;

	if(!CreateProcess(NULL, command, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
	{
		CString path;
		char szSysPath[MAX_PATH];
		GetEnvironmentVariable(_T("WinDir"), szSysPath, MAX_PATH);
		path = szSysPath;
		path += "\\system32\\";
		path += command;
		strcpy(command, path);
		if(!CreateProcess(NULL, command, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
		{
			return FALSE;
		}
	}

	// Set global child process handle to cause threads to exit.
	m_hChildProcess = pi.hProcess;

	// Close any unnecessary handles.
	if(!CloseHandle(pi.hThread))
	{
	}
	if(!CloseHandle(hOutputWrite))
	{
	}
	if(!CloseHandle(hInputRead))
	{
	}
	if(!CloseHandle(hErrorWrite))
	{
	}


	// Read the child's output.
	AfxBeginThread(ReadAndHandleOutput, this);
	// Redirection is complete

	return TRUE;
}

UINT CCmdWnd::ReadAndHandleOutput(LPVOID pParam)
{
	CCmdWnd *pWnd = (CCmdWnd*)pParam;
	HANDLE hPipeRead = pWnd->m_hPipeRead;
	CHAR *lpBuffer;
	DWORD nBytesRead;
	DWORD nCharsWritten;

	while(TRUE)
	{
		lpBuffer = new CHAR[1024];
		if(!ReadFile(hPipeRead, lpBuffer, 1020, &nBytesRead, NULL) || !nBytesRead)
		{
			if(GetLastError() == ERROR_BROKEN_PIPE)
			{
				delete [] lpBuffer;
				break; // pipe done - normal exit path.
			}
			else
			{
				delete [] lpBuffer;
				break;
				// Something bad happened.
			}
		}

		lpBuffer[nBytesRead] = '\0';
		pWnd->PostMessage(UWM_CONSOLEOUTPUT, 0, (LPARAM)lpBuffer);
	}

	pWnd->SendMessage(UWM_CHILDEXIT);
	return 0;
}

LRESULT CCmdWnd::OnConsoleOutput(WPARAM wParam, LPARAM lParam)
{
	m_output += (CHAR*)lParam;

	delete [] (CHAR*)lParam;

	Invalidate();

	return 0;
}

BOOL CCmdWnd::WriteToChild(CString text)
{
	m_output += text;
	char *lpszBuf = new char[text.GetLength()+1];
	strcpy(lpszBuf, text);
	DWORD nBytesWrite = strlen(lpszBuf);
	DWORD nBytesWrote;
	if(!WriteFile(m_hPipeWrite, lpszBuf, nBytesWrite, &nBytesWrote, NULL))
	{
		delete [] lpszBuf;
		if(GetLastError() == ERROR_NO_DATA)
			return FALSE; // Pipe was closed (normal exit path).
		else
			return FALSE;
	}

	delete [] lpszBuf;
	Invalidate();
	return TRUE;
}

LRESULT CCmdWnd::OnChildExit(WPARAM wParam, LPARAM lParam)
{
	CloseHandle(m_hPipeRead);
	CloseHandle(m_hPipeWrite);
	CloseHandle(m_hChildProcess);
	m_hChildProcess = NULL;

	Invalidate();
	return 0;
}</pre>]]></description>
         <link>http://www.photontech.org/dev/2006/12/redirecting_console_inputoutpu.html</link>
         <guid>http://www.photontech.org/dev/2006/12/redirecting_console_inputoutpu.html</guid>
        
          <category domain="http://www.sixapart.com/ns/types#category">C++/MFC</category>
        
        
          <category domain="http://www.sixapart.com/ns/types#tag">EntryMore</category>
        
         <pubDate>Sat, 30 Dec 2006 03:43:06 -0500</pubDate>
      </item>
      
      <item>
         <title>Recursively Enumerating the Files within a Directory and its Subdirectories</title>
         <description><![CDATA[By Brandon W. Yuille

I want to share a function that is very useful when dealing with file operations and management. The following function (void EnumDirFiles(...)), can be used as is to simply list all the files within a directory and all the files within the subdirectories of the directory. The usages for such a function are vast, as this is a basis for all file enumeration operations.

I hope you will find this as useful as I do!

<h3>Implementation</h3><pre>int main()
{
	EnumDirFiles("C:\\Program Files"); // List all files within the Program Files directory.

	return 0;
}</pre>

<h3>Recursive Function Definition</h3><pre>void EnumDirFiles(char *lpszDir)
{
	HANDLE hDir;
	BOOL bDone = FALSE;
	WIN32_FIND_DATA FileData;
	char szFind[512];

	strcpy(szFind, lpszDir);
	strcat(szFind, "\\*");
	hDir = FindFirstFile(szFind, &FileData); 
	if(hDir == INVALID_HANDLE_VALUE) 
	{ 
		printf("Invalid Directory: \"%s\"\n", lpszDir); 
		return;
	}

	while(!bDone) 
	{
		if(FileData.cFileName[0] != '.') // Exclude "." and ".."
		{
			char szFullFile[512];
			strcpy(szFullFile, lpszDir);
			strcat(szFullFile, "\\");
			strcat(szFullFile, FileData.cFileName);

			if(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			{
				printf("- Listing Directory: %s\n", szFullFile);
				EnumDirFiles(szFullFile);
			}
			else
				printf("%s\n", szFullFile);
		}

		if(!FindNextFile(hDir, &FileData)) 
		{
			if(GetLastError() == ERROR_NO_MORE_FILES) 
			{
				bDone = TRUE; 
			} 
			else 
			{
				printf("Unable to enumerate the next file.\n"); 
				bDone = TRUE;
			} 
		}
	} 

	// Close the search handle. 
	FindClose(hDir);
}</pre>]]></description>
         <link>http://www.photontech.org/dev/2006/11/recursively_enumerating_the_fi.html</link>
         <guid>http://www.photontech.org/dev/2006/11/recursively_enumerating_the_fi.html</guid>
        
          <category domain="http://www.sixapart.com/ns/types#category">C++/MFC</category>
        
        
          <category domain="http://www.sixapart.com/ns/types#tag">EntryMore</category>
        
         <pubDate>Thu, 30 Nov 2006 10:10:09 -0500</pubDate>
      </item>
      
      <item>
         <title>Introduction to Call Capture</title>
         <description>By Brandon W. Yuille

At first glance Call Capture may seem to be just another caller id program (like the ones that listen on your modem port). This is completely incorrect! Call Capture is a complete management/contact suite that is built to work with the Cisco IP Telephony system (expecting future versions to handle any IP phones that use SIP). Ever wonder why you switched to your new VoIP phone system (besides the low cost)? Well, you’re about to find out.

When running Call Capture, when someone calls, you get a notification via a fade-in window on the bottom right of your desktop that informs you of the person&apos;s name/company and other information. At this point you have some options: add this person to your contact directory, wait until you are finished with your conversation to add them to the later by navigating to your call history (which is almost infinite in space), or if they are already a contact in your system you can bring up all contact information that has been previously entered (by a coworker, yourself, ect) simply by clicking a button (I bet that customer will be amazed that you already know the entire history of their problem as soon as you answer).

Hopefully by that last point you&apos;re noticing the power of this application; real time accessible data by anyone in your company! There’s never a chance to miss a lead because you were unable to locate the contact’s information from Bob before he went home. Now you know everything Bob, John, Shirley, ect know when they know it.

Chances are, if you are reading this, you are involved in managing other employees. If so, then you know how important it is for them to gather as much information as possible on every single person who calls your company. Generally if someone calls you and you sell computers, they’re not looking to buy a car. That’s why every call you receive should be regarded as the best sales lead you’ll ever get! The problem lies in the fact that getting your employees to record this valuable information is almost impossible. Even if someone made a note of 2 inquiry calls chances are they are not telling you about the 30 they didn’t. Call Capture changes all this. Now you have an easy to use software suite linked in to all of your employees to view call history reports and other very useful information for determining the productivity of the individuals you are managing.

The Call Capture interface makes adding a contact about a 2 step process: when a person calls and they are not already in your contact list, you will be prompted with a new contact window to enter in some information about the person (don’t worry, we’ve implemented automatic name, company, and address information for you so you don’t have to bother with that). Then once you have all the information you need from the person, simply save their new contact window and instantly everyone in your company has access to this information. Now when you transfer this person over to tech support, they can bring up any and all information about this contact and the customer is saved from the annoyance of repeating themselves.

I’ve only tapped the surface of the Call Capture suite. As I said, this is only an introduction, but hopefully with the new knowledge of Call Capture you should have no more questions about why you bought that new phone system!
</description>
         <link>http://www.photontech.org/dev/2006/11/introduction_to_call_capture.html</link>
         <guid>http://www.photontech.org/dev/2006/11/introduction_to_call_capture.html</guid>
        
          <category domain="http://www.sixapart.com/ns/types#category">Call Capture</category>
        
        
          <category domain="http://www.sixapart.com/ns/types#tag">EntryMore</category>
        
         <pubDate>Mon, 27 Nov 2006 00:56:11 -0500</pubDate>
      </item>
      
      <item>
         <title>ESMTP Class</title>
         <description><![CDATA[By Brandon W. Yuille

Well, this is my first post on the PT Development Blog, so I thought I would start with some code that I know many will find useful.

This is a class I worte for a billing system to send out emails to customers when their invoices were ready to be viewed online.

Hope you'll be able to make use of this class!

<h3>Class Declaration</h3><pre>/**************************************************************
<br>	Name:	ESMTP Class
	Ver:	1.0.1.1
	Author:	Brandon W. Yuille

	Desc:	This class assumes MFC is used and all socket
			initializations have taken place in
			CWinApp::InitInstance(). Please don't consider this code
			as production code for any sort of SMTP client. This is
			simply provided so you can acomplish some simple tasks.
			If you make modifications to this code that are useful,
			please share them with others!

			This class is free to use by anyone.

	Copyright (C) 2006 Photon Technologies
**************************************************************/
#pragma once

#include &lt;vector&gt;

class CESMTP
{
public:
	CESMTP(void);
	~CESMTP(void);

	void AddRecipient(CString address, CString name = "");
	void SetFrom(CString address, CString name = "");
	void SetSubject(CString subject);
	void SetESMTPServerAddress(CString address);
	void SetESMTPServerPort(int port = 25);
	void SetMsgBody(CString data);

	BOOL SendMsg();

protected:
	BOOL RecvMessage(SOCKET *psock, CString requiredcode);

	struct NAMEADDR
	{
		CString name;
		CString addr;
	};

	std::vector&lt;NAMEADDR&gt; m_vto;
	NAMEADDR m_from;
	CString m_serveraddr;
	int m_serverport;
	CString m_data;
	CString m_subject;
};</pre>




<h3>Class Implementation</h3><pre>#include "StdAfx.h"
#include ".\esmtp.h"

CESMTP::CESMTP(void)
{
	m_serverport = 25;
}

CESMTP::~CESMTP(void)
{
}

void CESMTP::AddRecipient(CString address, CString name)
{
	NAMEADDR na;
	na.addr = address;
	na.name = name;

	m_vto.push_back(na);
}

void CESMTP::SetFrom(CString address, CString name)
{
	m_from.addr = address;
	m_from.name = name;
}

void CESMTP::SetSubject(CString subject)
{
	m_subject = subject;
}

void CESMTP::SetESMTPServerAddress(CString address)
{
	m_serveraddr = address;
}

void CESMTP::SetESMTPServerPort(int port)
{
	m_serverport = port;
}

void CESMTP::SetMsgBody(CString data)
{
	data.Replace("\r\n.\r\n", "\r\n..\r\n");
	if(data.Left(3) == ".\r\n")
		data.Insert(0, ".");

	m_data = data;
}

BOOL CESMTP::SendMsg()
{
	SOCKET sock;
	SOCKADDR_IN saremote;
	CString SendBuf;

	// Create the TCP/IP CCCP socket.
	if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
	{
		return FALSE;
	}

	saremote.sin_family = AF_INET;
	saremote.sin_port = htons((short)m_serverport);
	if(inet_addr(m_serveraddr.GetString()) == INADDR_NONE)
	{
		HOSTENT *hp;
		hp = gethostbyname(m_serveraddr.GetString());
		saremote.sin_addr.s_addr = *((unsigned long*)hp-&gt;h_addr);
	}
	else
	{
		saremote.sin_addr.s_addr = inet_addr(m_serveraddr.GetString());
	}

	// Connect to CCCP server.
	if(connect(sock, (SOCKADDR*)&saremote, sizeof(saremote)) == SOCKET_ERROR)
	{
		return FALSE;
	}

	// Recieve Banner
	if(!RecvMessage(&sock, "220"))
	{
		closesocket(sock);
		return FALSE;
	}

	// Send EHLO
	SendBuf = "EHLO\r\n";
	if(send(sock, SendBuf, SendBuf.GetLength(), 0) == SOCKET_ERROR)
	{
		closesocket(sock);
		return FALSE;
	}
	// Recieve OK
	if(!RecvMessage(&sock, "250"))
	{
		closesocket(sock);
		return FALSE;
	}

	// Send MAIL FROM:&lt;address@domain.com&gt;
	SendBuf = "MAIL FROM:" + m_from.addr + "\r\n";
	if(send(sock, SendBuf, SendBuf.GetLength(), 0) == SOCKET_ERROR)
	{
		closesocket(sock);
		return FALSE;
	}
	// Recieve OK
	if(!RecvMessage(&sock, "250"))
	{
		closesocket(sock);
		return FALSE;
	}

	for(int i = 0; i &lt; m_vto.size(); i++)
	{
		// Send RCPT TO:&lt;address@domain.com&gt;
		SendBuf = "RCPT TO:" + m_vto.at(i).addr + "\r\n";
		if(send(sock, SendBuf, SendBuf.GetLength(), 0) == SOCKET_ERROR)
		{
			closesocket(sock);
			return FALSE;
		}
		// Recieve OK
		if(!RecvMessage(&sock, "250"))
		{
			closesocket(sock);
			return FALSE;
		}
	}

	// Send DATA
	SendBuf = "DATA\r\n";
	if(send(sock, SendBuf, SendBuf.GetLength(), 0) == SOCKET_ERROR)
	{
		closesocket(sock);
		return FALSE;
	}
	// Recieve Begin DATA
	if(!RecvMessage(&sock, "354"))
	{
		closesocket(sock);
		return FALSE;
	}

	CString MsgBody;
	MsgBody += "Subject:  " + m_subject + "\r\n";
	MsgBody += "To: ";
	for(int i = 0; i &lt; m_vto.size(); i++)
	{
		MsgBody += " " + m_vto.at(i).name + " &lt;" + m_vto.at(i).addr + "&gt;;";
	}
	MsgBody += "\r\n\r\n";
	// Add the message data
	MsgBody += m_data;
	// End the message
	MsgBody += "\r\n.\r\n";

	SendBuf = MsgBody;
	if(send(sock, SendBuf, SendBuf.GetLength(), 0) == SOCKET_ERROR)
	{
		closesocket(sock);
		return FALSE;
	}
	// Recieve OK
	if(!RecvMessage(&sock, "250"))
	{
		closesocket(sock);
		return FALSE;
	}

	SendBuf = "QUIT\r\n";
	if(send(sock, SendBuf, SendBuf.GetLength(), 0) == SOCKET_ERROR)
	{
		closesocket(sock);
		return FALSE;
	}
	// Recieve Connection Closed
	if(!RecvMessage(&sock, "221"))
	{
		closesocket(sock);
		return FALSE;
	}

	closesocket(sock);
	return TRUE;
}

BOOL CESMTP::RecvMessage(SOCKET *psock, CString requiredcode)
{
	char szBuf[1025];
	CString Buffer;
	CString MsgCode;
	int BufLen = 0;
	int icount = 0;

	while((BufLen = recv(*psock, szBuf, 1024, 0)) &gt; 0)
	{
		szBuf[BufLen] = '\0';
		Buffer += szBuf;

		if(icount == 0)
			MsgCode = Buffer.Left(3);

		if(Buffer.Find(MsgCode + " ") != -1)
			break;

		icount++;
	}

	if(MsgCode == requiredcode)
		return TRUE;

	return FALSE;
}</pre>]]></description>
         <link>http://www.photontech.org/dev/2006/11/esmtp_class.html</link>
         <guid>http://www.photontech.org/dev/2006/11/esmtp_class.html</guid>
        
          <category domain="http://www.sixapart.com/ns/types#category">C++/MFC</category>
        
        
          <category domain="http://www.sixapart.com/ns/types#tag">EntryMore</category>
        
         <pubDate>Sat, 25 Nov 2006 05:17:18 -0500</pubDate>
      </item>
      
   </channel>
</rss>
