Internet Engineering Task Force Transport Area working Group NTERNET-DRAFT Mick O'Doherty draft-odoherty-half-pint-00.txt Nortel Networks July 2001 Half-Pint Status of this Memo This document is an Internet-Draft and is in full conformance with all provisions of Section 10 of RFC2026 [1]. Internet-Drafts are working documents of the Internet Engineering Task Force (IETF), its areas, and its working groups. Note that other groups may also distribute working documents as Internet-Drafts. Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet- Drafts as reference material or to cite them other than as "work in progress." The list of current Internet-Drafts can be accessed at http://www.ietf.org/ietf/1id-abstracts.txt The list of Internet-Draft Shadow Directories can be accessed at http://www.ietf.org/shadow.html. 1. Abstract This document defines a simple protocol for interaction between the PSTN and applications in the IP domain. It is not intended to be a full INAP or JAIN like interface, rather to easily enable some of the more common interactions in a way that will allow application developers, with minimal telephony knowledge, to quickly integrate it into new and existing applications. This document includes simple Java HTTP Servlets which implement some basic parts of the protocol as an example (note similar approaches would work for CGI also). To get a quick feel for the protocol go straight to the example section near the end. 2. Overview Half-Pint allows developers of web applications, or in fact any type of applications, to interact with the functionality provided by the Public Switched Telephony Network (PSTN) or any other telephony system that conforms to the Half-Pint protocol. It supports application to telephony system initiated interactions, such as creating calls between two telephony agents, as well as telephony system to application initiated interactions such as notification of incoming calls. OÆDoherty Page 1 Internet Draft Half-Pint July 2001 It is intended to be very easy to integrate into Web applications and to provide a quick way to interact with a small set of common services. In this way it is limited when compared with Intelligent Network (IN) or Java JAIN like protocols, and it is not intended to compete with these more complete solutions. The protocol is best described by looking at the messages and procedures that compose it and the remainder of this document follows this format. A quick way to get a feel for the protocol is to go straight to the example section at the end. The appendices include an example Java Servlet implementation that shows how a message might be generated and received. 3. General format of messages The protocol is defined in this document using the ABNF scheme defined in [3]. A half-pint message is defined as: Half-Pint-message = HalfPintVersion Addressee Sender TransactionID [ AuthenticationInfo ] MessageTypeandParameterPair HalfPintVersion = (ôHalfPintVersionö | ôvö) ô:ö *token Addressee = (ôAddresseeö | ôaö) ô:ö absoluteURI Sender = (ôSenderö | ôsö) ô:ö absoulteURI TransactionID = (ôTransactionIDö | ôtö) ô:ö token [ ô@ö token] AuthenticationInfo = (ôAuthenticationInfoö | ôaiö) ô:ö *token Token = 1*< any CHAR except CTL's or separators> ; as in SIP spec [2] CTL = MessagetTypeandParameterPair = (ôMessagetTypeö | ômö) ô:ö ( ôCreateCallö CreateCallParameterSet | ôAddPartyToCallö AddPartyToCallParameterSet | ôBookConferenceCallö BookConferenceCallParameterSet | ôConferenceCallResponseö ConferenceCallResponseParameterSet | ôGeneralResponseö GeneralResponseParameterSet | ôRegisterCallAlertö RegisterCallAlertParameterSet | ôCancelCallAlertö CancelCallALertParameterSet | ôGetCallLogö GetCallLogParameterSet | ôClearCallLogö ClearCallLogParameterSet | ôCallLogö CallLogParameterSet OÆDoherty Page 1 Internet Draft Half-Pint July 2001 | ôCallAlertö CallAlertParameterSet | ôCallAlertResponseö CallAlertResponseParameterSet | ôCallAlertErrorö CallAlertErrorParameterSet | ôSetSubscriberServiceö SetSubscriberServiceParameterSet | ôQuerySubscriberServiceö QuerySubscriberServiceParameterSet | ôSubscriberServiceStatusö SubscriberServiceStatusParameterSet | ôVoiceMailBoxQueryö VoiceMailBoxQueryParameterSet | ôVoiceMailBoxResponseö VoiceMailBoxResponseParameterSet | ôVoiceMailRequestö RetrieveVoiceMailParameterSet | ôVoiceMailö VoiceMailParameterSet ) An example of a CreateCallMessage is shown below û the additional parameters are explained later in the document: HalfPintVersion : 1.0 Addressee : teleservice@teleprovider.org Sender : 47.23.10.10 TransactionID : 12345 AuthenticationInfo : X1943667 MessageType : CreateCall CallingParty : +441628770770 CalledParty : +12137632212 AnnouncementID : 5t443r CompletionNotification : n 4. Message addressing, transaction id, and authentication info The Addressee, Sender and TransactionID parts of the message as defined above allow a receiver of a message to know who the message is for, who it is from and give a globally unique identifier for the transaction in the TransactionID. A transaction is a logical groupings of messages, such as a CreateCall and a Response or a CallAlert and a CallAlertError. In general, any message which is sent in reply to another message, should use the transaction-id in the original message. In general it is expected that messages will be sent directly from the receiver to the sender, so the receiver of the message will just check that the message is from them (by looking at the Addressee field) and then act on it. However, messages can be proxied in which case the receiver looks at the Addressee and (if it decides to forward the message) sends it on to the receiver (or the next proxy in the path). The addressee and sender are defined as standard absolute URIÆs for this version of the document û further study is required to decide how best to represent these fields (for example do we need a new URL scheme etc). In the case where a port number is not specified a default port of 7071 will be used. The TransactionID is similar to the call-id in the SIP protocol [2] from which this definition is taken and is simply to allow a particular request be uniquely identified: it MUST be a globally unique identifier and MUST NOT OÆDoherty Page 1 Internet Draft Half-Pint July 2001 be reused for later calls. Use of cryptographically random identifiers is RECOMMENDED. Implementations MAY use the form "localid@host". Call-IDs are case-sensitive and are simply compared byte-by-byte. Authentication info is used to identify the sender of a message and if necessary to decide if they have permission to use a particular service. The implementation of Authentication info is a topic for further study. 5. General message sending and responding procedures When a Half Pint message is received that needs to be replied to, the reply MUST be sent to the destination specified in the Sender field in the message. All Half Pint messages MUST have the destination address specified in the Addressee field of the message and this is the destination which the message must be delivered to by whatever transport system is being used. 6. Call Initiation messages and procedures The following messages are defined to allow an application request that a call be initiated between two specified parties by the PSTN interface proxy: 6.1 CreateCall Overview -------- CreateCall is used to request that a telephony proxy create a call between the parties identified in the parameters of the message. The call will be created by first ringing the æcalling partyÆ and presenting them with ringing tone and then establishing a call between them and the æcalled partyÆ. When the called party answers the call is fully set up. Message Parameters ------------------ The definition of the parameters for CreateCall is: CreateCallParameterSet = CallingParty CalledParty [AnnouncementID] CompletionNotification [CLIPresentation] CallingParty = (ôCallingPartyö | ôcpö) ô:ö *token CalledParty = (ôCalledPartyö | ôcdpö) ô:ö*token AnnouncementID = (ôAnnouncementIDö | ôaidö) ô:ö *token CompletionNotification = (ôCompletionNotifictaionö | ôcnö) ô:ö (ôyö | ônö) CLIPresentation = (ôCLIPresentation ö | ôcliö) ô:ö Restrict | Present Restrict = ôRestrictö | ôrö Present = ôPresentö | ôpö OÆDoherty Page 1 Internet Draft Half-Pint July 2001 Procedures for the generator of the message ------------------------------------------- The application or entity generating this message, having built the message with all the correct information including the TransactionID, should wait for a GeneralResponse with a matching TransactionID to confirm that the telephony proxy that the message was sent to received the message. The General response will indicate if the request was successful or not. This indicates that the telephony proxy has agreed to make the requested call, not that the call has already been successfully set up. If no response is received within a certain time the application may decide to resend the message until 5 attempts have been made. The timeout period is left as implementation detail. Additionally, if an announcementID is included the receiver MUST play the announcement before setting up the call. If they do not have the ability to do this, then they MUST indicate that they cannot service the entire request with a General Request as mentioned above. The value of the announcement ID must be something that the receiver of the message will understand. How this is communicated is left to a implementation/deployment decision (for instance a service provider may provide a special code for a customer announcement or may provide a list of standard announcement ids. Additionally they may provide some secure facility for a customer to load there own announcement id and then provide a unique id for it). If the application has requested CompletionNotification then it should be ready to accept an additional GeneralResponse message, indictaing either that the call has been successfully set up (responseType = æokÆ) or that it has not been set up for some reason (response type = æerrorÆ). In both these cases the responseText field MUST begin with æCreateCallConfirmationÆ. If the application wishes to specify whether the CLI should be restricted or not it can specify this with the CLIPresentation parameter. If the parameter is not set the default setting for the user is set. Note that if this parameter is used it must be checked that it is supported by and within the rules of the telephony network. Procedures for the receiver of the message ------------------------------------------ The receiver of the message should determine if it has the capability to service this request and if it does it should return a GeneralResponse message with a ResponseType of æOKÆ. If it does not it should return a GeneralResponse message with a ResponseType of æCannotServiceRequestÆ. If it is servicing the request the receiver must either directly, or through some other mechanism set up a call firstly to the CallingParty, connecting them to a ringing tone, and then establish a call to the CalledParty. If an AnnouncementID was specified, the CallingParty is first connected to the announcement before being connected to the RingingTone (or the CalledParty if the Announcement is used in place of the RingintTone). If the AnnouncementID is not receognised a GeneralResponse with an ResponseType of OÆDoherty Page 1 Internet Draft Half-Pint July 2001 æErrorÆ should be returned. Similarly, if CLIPresentation was specified as either Present or Restrict and this cannot be done, then a GeneralResponse with an ResponseType of ôCannotServiceRequestö MUST be sent and additionally a ResponseText reason in a human readable format can be given. If the application which sent the CreateCall message indicated that it wanted notification of call setup, an additional GeneralResponse message must be sent back to the application indicating whether the call was set up successfully or not û if the call was set up successfully the responseType Parameter is set to æokÆ and if not then it is set to æerrorÆ. The responseText parameter MUST begin with the text æCreateCallConfirmationÆ. 6.2 AddPartyToCall Overview -------- AddPartyToCall is used to request that a telephony proxy add a party to an existing call. The call is identified by one of the subscriber numbers involved in the call. Message Parameters ------------------ The definition of the parameters for AddPartyToCall is: AddPartyToCallParameterSet = SubscriberNumToAdd SubscriberNumber [CLIPresentation] SubscriberNumToAdd = (ôSubscriberNumToAddö | ôsnaö) ô:ö *token SubscriberNumber = (ôSubscriberNumberö | ôsnö) ô:ö*token CLIPresentation = (ôCLIPresentation ö | ôcliö) ô:ö Restrict | Present Restrict = ôRestrictö | ôrö Present = ôPresentö | ôpö Procedures for the generator of the message ------------------------------------------- The application or entity generating this message, having built the message with all the correct information including the TransactionID, should wait for a GeneralResponse with a matching TransactionID and a ResponseType of æokÆ to confirm that the new party was added to the call, or a ResponseType other than æokÆ if the Party was not added to the call. Procedures for the receiver of the message ------------------------------------------ The receiver of the message should determine if it has the capability to service this request and if it does it should return a GeneralResponse message with a ResponseType of æOKÆonce it has added the party. If it does OÆDoherty Page 1 Internet Draft Half-Pint July 2001 not it should return a GeneralResponse message with a ResponseType of æCannotServiceRequestÆ. If it determines that the Subscriber number which identifies the call that that the new party is to be added to is not involved in a call, it again returns a GeneralResponse message with a ResponseType of æerrorÆ and may additionally indicate in the ResponseText that the SubscriberNumber was not in a call. If CLIPresentation is present and is set to a value that it cannot comply with, it should send a GeneralResponse message with a ResponseType of æCannotServiceRequestÆ and may additionally indicate in the ResponseText why it was not able to comply in a human readable form. 6.3 BookConferenceCall Overview -------- BookConferenceCall is used to request that a telephony proxy create a conference call at a specified time and for a specified maximum number of participants. The telephony proxy will return host and participant dial in numbers, as well as password for each in a ConferenceCallResponse message. Message Parameters ------------------ The definition of the parameters for BookConferenceCall is: BookConferenceCallParameterSet = StartTime EndTime MaxParticipants StartTime = (ôStartTimeö | ôsö) ô:ö DateAndTime EndTime = (ôEndTimeö | ôeö) ô:ö DateAndTime MaxParticipants = (ôMaxParticipantsö | ômpö) ô:ö *Digit DateAndTime = (ôDateAndTimeö | ôdö) ô:ö Date Time Date = Day.Month.Year Time = hour.min Day = ôMonö | ôTueö | ôWedö | ôThurö | ôFriö | ôSatö | ôSunö Month = firstdigit.seconddigit Year = 4*4Digit Firstdigit = %x30-31; can be ô0ö or ô1ö Seconddigit = %x30-39; can be ô0ö to ô9ö Hour = hourfirstdigit.hourseconddigit Hourfirstdigit = %x30-32; can be ô0ö or ô1ö or ô2ö Hourseconddigit = %x30-39; can be ô0ö to ô9ö Min = Minfirstdigit.Minseconddigit Minfirstdigit = %x30-35; can be ô0ö or ô1ö or ô2ö or ô3ö or ô4ö or ô5ö Minseconddigit = %x30-39; can be ô0ö to ô9ö OÆDoherty Page 1 Internet Draft Half-Pint July 2001 Procedures for the generator of the message ------------------------------------------- The application or entity generating this message, having built the message with all the correct information including the TransactionID, should wait for a ConferenceCallResponse with a matching TransactionID to confirm that the telephony proxy that the message was sent to received the messageand has agreed to set up the conference call. If the telephony proxy has not agreed to set up the call this will be indicated in the ConferenceCallResponse. If no response is received within a certain time the application may decide to resend the message until 5 attempts have been made. The timeout period is left as implementation detail. If the telephony Proxy has agreed to set up the call then the application should store and/or pass on to the user(s) the host and participant numbers and passwords. Procedures for the receiver of the message ------------------------------------------ The receiver of the message should determine if it has the capability to service this request and if it does and the resources are available at the requested time, it should return a ConferenceCallResponse message with a confirmation parameter of æyÆ. If it does not it should return a ConferenceCallResponse message with a confirmation parameter of ænÆ. If it is servicing the request it must also include the host and participant numbers and paswords. 6.4 ConferenceCallResponse Overview -------- ConferenceCallResponse is used by a telephony proxy to respond to a BookConferenceCall message. Message Parameters ------------------ The definition of the parameters for ConferenceCallResponse is: ConferenceCallResponseParameterSet = Confirmation [HostNumber HostPassword ParticipantNumber ParticipantPassword ] Confirmation = (ôConfirmationö | ôcö) ô:ö (ôyö | ônö) HostNumber = (ôHostNumberö | ôhnö) ô:ö*token HostPassword = (ôHostPasswordö | ôhpö) ô:ö*token ParticipantNumber = (ôParticipantNumberö | ôpnö) ô:ö*token ParticipantPassword = (ôParticipantPasswordö | ôppö) ô:ö*token OÆDoherty Page 1 Internet Draft Half-Pint July 2001 Procedures for the generator of the message ------------------------------------------- The application or entity generating this message, having built the message with all the correct information including the TransactionID, sends the message and does not expect a response. The host number and participant numbers refer to the numbers the host and the participants should dial in with. The password indicates the password they should enter when prompted. Procedures for the receiver of the message ------------------------------------------ The receiver of the message will receive this in response to a BookConferenceCall message and should cancel any timers associated with the transaction. 7. Call Alert messages and procedures These messages allow a telephony proxy to notify an application about an incoming call and provide a number of alternative actions that the application can take in response. 7.1 RegisterCallAlert Overview -------- RegisterCallAlert is used by a subscriber to indicate that they want to be notified with a CallAlert message when a call is received on the specified number. It may also be used by an application to indicate that it wants to be notified of incoming calls to a particular number (the telephony proxy must decide based on the authentication information whether to grant this to a particular user to avoid misuse of this feature û this is an implementation and deployment decision). Message Parameters ------------------ The definition of the parameters for RegisterCallAlert is: RegisterCallAlertParameterSet = SubscriberNumber *URItoAlert SubscriberNumber = (ôSubscriberNumberö | ôsnö) ô:ö *token URItoAlert = (ôURItoAlertö | ôUö) ô:ö absoluteURI Procedures for the generator of the message ------------------------------------------- The application or entity generating this message, having built the message with all the correct information including the TransactionID, should wait for a GeneralResponse with a matching TransactionID to confirm that the telephony proxy has accepted this request and will generate CallAlert Messages when appropriate and send them to each URIlisted in the URI to alert list. OÆDoherty Page 1 Internet Draft Half-Pint July 2001 If there was already registered URIÆs to be aletered for this Subscriber number the new URIÆs are added to the list to be alerted. Message timeout and retry timeout handling are as for the CreateCall message. Procedures for the receiver of the message ------------------------------------------ The receiver of the message should determine if it has the capability to service this request and if it does it should return a GeneralResponse message with a ResponseType of æokÆ. If it does not it should return a GeneralResponse message with a ResponseType of æCannotServiceRequestÆ. 7.2 CancelCallAlert Overview -------- CancelCallALert is used by a subscriber to indicate that they no longer want to be notified with a CallAlert message when a call is received on the specified number. It may also be used by an application to indicate that it no longer wants to be notified of incoming calls to a particular number (the telephony proxy must decide based on the authentication information whether to grant this to a particular user to avoid misuse of this feature û this is an implementation and deployment decision). Message Parameters ------------------ The definition of the parameters for CancelCallAlert is: CancelCallAlertParameterSet = SubscriberNumber [*URItoAlert] SubscriberNumber = (ôSubscriberNumberö | ôsnö) ô:ö *token URItoAlert = (ôURItoAlertö | ôUö) ô:ö absoluteURI Procedures for the generator of the message ------------------------------------------- The application or entity generating this message, having built the message with all the correct information including the TransactionID, should wait for a GeneralResponse with a matching TransactionID to confirm that the telephony proxy has accepted this request and will not generate CallAlert messages to the specified URI when a call arrives on the specified number. If there are no URItoAlertÆs in the message then all the previously registered URIÆs for this subscriber number are cancelled. Message timeout and retry timeout handling are as for the CreateCall message. Procedures for the receiver of the message OÆDoherty Page 1 Internet Draft Half-Pint July 2001 ------------------------------------------ The receiver of the message should cancel any notification associated with this number and send back a successful general response message. If it had no notification set on this number it should still reply with a Successful General Response message. 7.3 CallAlert Overview -------- CallAlert is used to indicate that a Call has been received on a particular subscriber number. It may contain a set of allowed actions that may be included in a CallAlertResponse message. Message Parameters ------------------ The definition of the parameters for CallAlert is: CallAlertParameterSet = CallingParty CalledParty [ForwardedFromParty] *AlertParameters CallingParty = (ôCallingPartyö | ôcpö) ô:ö *token CalledParty = (ôCalledPartyö | ôcdpö) ô:ö *token ForwardedFromParty = (ôForwardedFromPartyö | ôfpö) ô:ö *token AlertParameters = ContactURL | CallingName | CallingPictureURL | ActionOption ContactURL = (ôContactURLö | ôctuö) ô:ö AbsoluteURI CallingName = (ôCallingNameö | ôclnö) ô:ö *token CallingPictureURL = (ôCallingPictureURLö | ôpicUö) ô:ö AbsoluteURI ActionOption = ActionLabel [ActionText] ActionText = *Token ActionLabel = PreDefinedAction | SenderDefinedAction SenderDefinedAction = *Token PreDefinedAction = ôRejectCallö | ôForwardCallToö ô:ö *token | ôSendToVoiceMailö Procedures for the generator of the message ------------------------------------------- The telephony proxy generates this message for each device that is registered to be notified when a call is received on this number. It builds the messages with all the applicable headers including a transaction-id per message and sends them out. The ActionOptions fields indicate the actions (if any) that the receiver of the message may choose and send back in a CallAlertResponse message. The Actions may be one or more of the set of predefined actions, or a sender defined action represented by a text string. The actions, whether predefined or User defined may be accompanied by a text OÆDoherty Page 1 Internet Draft Half-Pint July 2001 string which should be suitable for display to a human user, if the receiving application is capable of and wishes to present it. If the call was previously forwarded and this information is available it should be included in the ForwardedFromParty parameter. Once the message has been sent a Response is expected and the sender should wait for this. Message retry and timeout is the same as for Create Message and the timeout period is left to the implementation, but for this case it must allow for the telephony call which is associated with this message and which will almost certainly have its own timers and timeouts. When the Response is received, the telephony Proxy should apply whatever action the Response indicates. If no Action is indicated, or if the original message did not include any action options, then the message is simply a confirmation that the message was received and the call telephony Server should apply the default action, which is to allow the call to continue as it would normally. If the Response contains an action which is not supported by the Telephony Proxy or which it does not wish to allow for this subscriber number or sending application, or the response does not correspond to any call or CallAlert message sent by the telephony Proxy it MUST reply to the message with a CallAlertError message with a the reason set appropriately and optionally a Human readable error message. Once the first response has been received all further responses (from other devices the CallAlert was sent to for example) are ignored and a CallAlertError message is sent back to the sender with a code of æNoCallAlertSentÆ. Procedures for the receiver of the message ------------------------------------------ The receiver of the message should look at the allowed actions (if any) and decide by whatever means it wants (e.g. interaction with a human user, rules table, algorithm etc) which action (if any) it wants to take. If it does not recognize any of the actions in the case where the actions are user defined, then it simply responds with a CallALert message with no Action included. The same reply is used for the case where the CallAlert contained no ActionOptions. 7.4 CallAlertResponse Overview -------- CallAlertResponse is sent in reply to a CallAlert message to acknowledge receipt of the CallAlert and to indicate which (if any) of the allowed Actions has been selected. Message Parameters ------------------ The definition of the parameters for CallAlertResponse is: OÆDoherty Page 1 Internet Draft Half-Pint July 2001 CallAlertResponseParameterSet = ActionLabel Procedures for the generator of the message ------------------------------------------- This message is sent in response to a CallAlert Message so the procedures are as for the receiver of CallAlert Messages above. Procedures for the receiver of the message ------------------------------------------ If the receiver has sent a CallAlert which led to this response then the procedures are as defined in the generator of CallAlert messages above. If the receiver had not generated a CallAlert message and hence did not expect this message then a CallAlertError message is generated with a reason of æNoCallAlertSentÆ and optionally a human readable error message in the CallError parameter. 7.5 CallAlertError Overview -------- CallAlertError is used to indicate that an Error has been generated during a CallAlert interaction, usually because the CallAlertResponse has some problem with it. Message Parameters ------------------ The definition of the parameters for CallAlert is: CallAlertErrorParameterSet = ErrorType ErrorMessage ErrorType = ôNoCallAlertSentö | ôResponseFromOtherDeviceReceivedö | ôUnknownActionö | ôActionNotAllowedö | ôCallErrorö ErrorMessage = *Token Procedures for the generator of the message ------------------------------------------- The cases in which a CallAlert Error message can be generated are described in the CallAlert and CallAlertResponse sections above. The generator builds and sends the message with the appropriate error code and an error message which can be presented to a human user if appropriate. Procedures for the receiver of the message ------------------------------------------ OÆDoherty Page 1 Internet Draft Half-Pint July 2001 The receiver of the message should look at Error code and Error message and raise an alarm or notify the user as appropriate. 8. Call Log Messages These messages allow an application or user query and manipulate call log details for a specified subscriber number. 8.1 GetCallLog Overview -------- GetCallLog is used to request a log of incoming, outgoing or both incoming and outgoing calls from a telephony proxy. The requester can specify how many calls back in the call history they want to go. Message Parameters ------------------ The definition of the parameters for GetCallLog is: GetCallLogParameterSet = SubscriberNumber Direction [NumberOfCalls] SubscriberNumber = (ôSubscriberNumberö | ôsnö) ô:ö *token Direction = (ôDirectionö | ôDö) ô:ö DirectionString DirectionString = (ôIncomingö | ôIö) |(ôOutgoingö | ôOö) | (ôbothö | ôbö) NumberOfCalls = Integer Procedures for the generator of the message ------------------------------------------- The application or entity generating this message, having built the message with all the correct information including the TransactionID, should wait for a CallLog message with a matching TransactionID. Message timeout and retry timeout handling are as for the CreateCall message. Note that the NumberOfCalls Parameter can be used to get the last 5, 10 etc calls, or simply the last one call making this effectively a get last call message. Procedures for the receiver of the message ------------------------------------------ The receiver of the message should reply with a CallLog message for the specified direction(s) if it is capable of servicing the request or with a GeneralResponse message with a ResponseType of æCannotServiceRequestÆ if it is not. 8.2 ClearCallLog OÆDoherty Page 1 Internet Draft Half-Pint July 2001 Overview -------- ClearCallLog is used to Clear the Call Log for the specified direction(s) on the telephony proxy for a particular Subscriber number. Message Parameters ------------------ The definition of the parameters for ClearCallLog is: ClearCallLogParameterSet = SubscriberNumber Direction SubscriberNumber = (ôSubscriberNumberö | ôsnö) ô:ö *token Direction = (ôDirectionö | ôDö) ô:ö DirectionString DirectionString = (ôIncomingö | ôIö) |(ôOutgoingö | ôOö) | (ôbothö | ôbö) Procedures for the generator of the message ------------------------------------------- The application or entity generating this message, having built the message with all the correct information including the TransactionID, should wait for a General Response message with a matching TransactionID. Message timeout and retry timeout handling are as for the CreateCall message. Procedures for the receiver of the message ------------------------------------------ The receiver of the message should reply with a GeneralREsponse message with a ResposneType of æokÆ if it is capable of servicing the request or with a GeneralResponse message with a ResponseType of æCannotServiceRequestÆ if it is not. 8.3 CallLog Overview -------- CallLog is used to send a Call Log to an entity that has requested one. Message Parameters ------------------ The definition of the parameters for CallLog is: CallLogParameterSet = SubscriberNumber * (Direction CallLogReport) SubscriberNumber = (ôSubscriberNumberö | ôsnö) ô:ö *token Direction = (ôDirectionö | ôDö) ô:ö DirectionString DirectionString = (ôIncomingö | ôIö) |(ôOutgoingö | ôOö) | (ôbothö | ôbö) OÆDoherty Page 1 Internet Draft Half-Pint July 2001 CallLogReport = *token| url Procedures for the generator of the message ------------------------------------------- The application or entity generating this message, having built the message with all the correct information including the TransactionID, sends the message and does not expect a response. The Log Report will consist of a field indicating the direction and then the text for the Call Log for that direction. The format of the Call Log is left to implementations, but HTML, XML or standard Text are probably appropriate. If the length of the callLog is too long for the transport system (for instance if UDP is being used and the total message length exceed 1500 bytes) it may be more appropriate to send a URL to the call log, which the receiver can then use to retrieve it. Procedures for the receiver of the message ------------------------------------------ The receiver of the message will receive this in response to a GetCallLog message. If the message contains a URL for the call log the receiver should access this URL to retrieve the log. 9. Subscriber Service Management Services These messages allow a subscribers service to be set and queried through the telephony Proxy. The format of the service specific information is not specified as part of this document (for now). 9.1 SetSubscriberService Overview -------- This message is used to set a service for a particular telephony subscriber (directory number). Note that setting the service means set it to some state, not necessarily just setting it on û this message can be used to set a service off also for example. Message Parameters ------------------ The definition of the parameters for SetSubscriberService is: SetSubscriberServiceParameterSet = SubscriberNumber ServiceName ServiceInfo SubscriberNumber = (ôSubscriberNumberö | ôsnö) ô:ö *token ServiceName = (ôServiceNameö | ôsvnö) ô:ö *token ServiceInfo = (ôServiceInfoö | ôsviö) ô:ö *token Procedures for the generator of the message ------------------------------------------- The generator of this message is responsible for setting the Service name and Service info appropriately, so that the telephony proxy recognizes it. OÆDoherty Page 1 Internet Draft Half-Pint July 2001 For now there is no format specified for this information, as many services are possible with diverse parameter requirements. It is up to the sender of the message to use a scheme that will be understood by the receiver. It may be worth in the future allowing a scheme to specify the format of this information (e.g. by referring to a XML DTD)- this can be left for further study for now. The generator of the message waits for a General Response message which will indicate either that the request was successfully serviced or that there was an error. Message retry and timeout is the same as for Create Message Procedures for the receiver of the message ------------------------------------------ The receiver of the message should service the request if it can and return a General Response message with the ResponseType set to æokÆ. If it cannot service the response (e.g. it does not recognize the service info format) it should repond with a GeneralResponse with the ResponseType set to æCannotServiceRequestÆ and optionally a human readable error message in the ResponseText. 9.2 QuerySubscriberService Overview -------- This message is used to query the status of a service or a number of services for a particular telephony subscriber (directory number). Message Parameters ------------------ The definition of the parameters for QuerySubscriberService is: QuerySubscriberServiceParameterSet = SubscriberNumber [ *ServiceName ] SubscriberNumber = (ôSubscriberNumberö | ôsnö) ô:ö *token ServiceName = (ôServiceNameö | ôsvnö) ô:ö *token Procedures for the generator of the message ------------------------------------------- The generator of this message generate the message with either the names of all the services that they want status info on, with no service names in which case the status of all services will be returned. The generator of the message waits for a SubscriberServiceStatus message which will indicate either that the request was successfully serviced or a GeneralResponse message which will indicate that there was an error. Message retry and timeout is the same as for Create Message Procedures for the receiver of the message OÆDoherty Page 1 Internet Draft Half-Pint July 2001 ------------------------------------------ The receiver of the message should service the request if it can and return a SubscriberServiceStatus message. If it cannot service the response (e.g. it does not recognize the service info format) it should respond with a GeneralResponse with the ResponseType set to æCannotServiceRequestÆ and optionally a human readable error message in the ResponseText. 9.3 SubscriberServiceStatus Overview -------- This message is used to report the status of a service or a set of services for a particular telephony subscriber (directory number). Message Parameters ------------------ The definition of the parameters for SubscriberServiceStatus is: SubscriberServiceStatusParameterSet = SubscriberNumber *[ServiceName ServiceInfo] SubscriberNumber = (ôSubscriberNumberö | ôsnö) ô:ö *token ServiceName = (ôServiceNameö | ôsvnö) ô:ö *token ServiceInfo = (ôServiceInfoö | ôsviö) ô:ö *token Procedures for the generator of the message ------------------------------------------- This message is generated in response to a QuerySubscriberService message. Procedures for the receiver of the message ------------------------------------------ The receiver of the message checks the Status and makes it available to the end user (or another process). 10. Voicemail Messages These message allow a user or application interact with a subscribers voicemail via the telephony Proxy. 10.1 VoiceMailBoxQuery Overview -------- VoiceMailBoxQuery is used to query the status of the mailbox for a particular subscriber number. Message Parameters ------------------ The definition of the parameters for VoiceMailBoxQuery is: OÆDoherty Page 1 Internet Draft Half-Pint July 2001 VoiceMailBoxQueryParameterSet = SubscriberNumber SubscriberNumber = (ôSubscriberNumberö | ôsnö) ô:ö *token Procedures for the generator of the message ------------------------------------------- The generator of this message build the complete message including the TransactionID and sends the message to the telephony proxy. Once the message has been sent a Response is expected and the sender should wait for this. Message retry and timeout is the same as for Create Message and the timeout period is left to the implementation. If the request is successful a VoiceMailBoxResponse message will be received. If there is a problem a GeneralResponse message with an appropriate error code will be received. Procedures for the receiver of the message ------------------------------------------ The receiver of the message should determine if it can service the request and if so it should do so and return a VoiceMailBoxResponse message. If it cannot service the request it should send a GeneralResponse message with the ResponseType set to æCannotServiceRequestÆ. 10.2 VoiceMailBoxResponse Overview -------- VoiceMailBoxResponse is used to reply to a VoiceMailBoxQuery message, providing the status of the mailbox for a particular subscriber number. Message Parameters ------------------ The definition of the parameters for VoiceMailBoxResponse is: VoiceMailBoxResponseParameterSet = SubscriberNumber NumberOfVoiceMails *[VoiceMailRecord] SubscriberNumber = (ôSubscriberNumberö | ôsnö) ô:ö *token NumberofVoiceMails = (ôNumberOfVoiceMailsö | ônvö) ô:ö *Digit VoiceMailRecord = VoiceMailID CallingNumber DateAndTime Duration ReadTag [Urgency] VoiceMailID = (ôVoiceMailIDö | ôvmiö) ô:ö *token CallingNumber = (ôCallingNumberö | ôcnö) ô:ö *token DateAndTime = (ôDateAndTimeö | ôdö) ô:ö Date Time; see BookConferenceCall OÆDoherty Page 1 Internet Draft Half-Pint July 2001 Duration = (ôDuration ö | ôduö) ô:ö *digit ReadTag = (ôReadTagö | ôrö) ô:ö (ôyö | ônö) Urgency = (ôUrgencyö | ôuö) ô:ö *token Procedures for the generator of the message ------------------------------------------- The generator of this message builds this message in response to a VoiceMailQQueryMessage. Procedures for the receiver of the message ------------------------------------------ The receiver of the message takes the information in the message and makes it available to a user or another application. 10.3 VoiceMailRequest Overview -------- VoiceMailRequest is used to request an action on a particular voicemail for a given subscriber number. Message Parameters ------------------ The definition of the parameters for VoiceMailRequest is: VoiceMailRequestParameterSet = SubscriberNumber VoiceMailID RequestType SubscriberNumber = (ôSubscriberNumberö | ôsnö) ô:ö *token VoiceMailID = (ôVoiceMailIDö | ôvmiö) ô:ö *token RequestType = (ôRequestTypeö | ôrö) ô:ö RetreiveRequestType | ôDeleteö | ForwardRequestType RetreiveRequestType = (ôRetrieve in formatö | ôrfö) ô:ö *token ForwardRequestType = (ôForward toö | ôftö) ô:ö *token Procedures for the generator of the message ------------------------------------------- The generator of this message must know the specific VoiceMailID that they want the request to apply to. They then build and send the message and wait for a VoiceMailResponse message, or a GeneralResponse message. Timeout is as for the CreateCall message. Procedures for the receiver of the message ------------------------------------------ The receiver of the message looks at the VoiceMailID and the requesttype and determines if it can service the request. If it can it does so and replies with a GeneralResponse message with the ResponseType set to æokÆ, or if OÆDoherty Page 1 Internet Draft Half-Pint July 2001 retrieval of a voicemail was requested with a VoiceMail message. If it cannot it returns a GeneralResponse with the ResponseType set to æCannotServiceRequestÆ. In the case of a æretreive in formatÆ request where the format is not recognised or not supported, the receiver replies with a GeneralResponse message with the ResponseType set to æcannotServiceRequestÆ and optionally the ResponseText explaining in a human readable form that the requested format was not recognised or not supported. 10.4 VoiceMail Overview -------- VoiceMail is used to reply to a VoiceMailRequest with a Voicemail. Message Parameters ------------------ The definition of the parameters for VoiceMailResponse is: VoiceMailResponseParameterSet = SubscriberNumber VoiceMailID Format VoiceMail SubscriberNumber = (ôSubscriberNumberö | ôsnö) ô:ö *token VoiceMailID = (ôVoiceMailIDö | ôvmiö) ô:ö *token Format = (ôFormatö | ôfö) ô:ö *token VoiceMail = (ôVoiceMailö | ôvö) ô:ö Vmail Vmail = *token | absoluteURI Procedures for the generator of the message ------------------------------------------- This message is generally sent in response to a VoiceMailRequest message. The sender of this message (VoiceMail) must ensure that the format field matches the format of the Voicemail sent. It may be better in many cases to send a URI for the voicemail than the voicemail itself. If it is included in the message directly, then the format field tells the receiver how to interpret the data represented by *token. Procedures for the receiver of the message ------------------------------------------ The receiver of the message looks at the format field and uses it to present the VoiceMail to a user or another application. 11. Misc Messages Messages whose scope is general. 11.1 GeneralResponse OÆDoherty Page 1 Internet Draft Half-Pint July 2001 Overview -------- GeneralResponse is used to respond to a received message when there is no specific message for the purpose. Message Parameters ------------------ The definition of the parameters for GeneralResponse is: GeneralResponseParameterSet = ResponseType [Responsetext] ResponseType = (ôResponseTypeö | ôrtö) ô:ö ( ôOKö | ôErrorö | ôUnexpectedMessageö | ôUnrecognisedMessageö | ôCannotServiceRequestö | *token ) ResponseText = (ôResponseTextö | ôrtxö) ô:ö *token Procedures for the generator of the message ------------------------------------------- This generator builds the message with the TransactioID equal to the received message and the ResponseType set appropriately. The optional ResponseText can be used to include an error message that is understandable by a human user. Procedures for the receiver of the message ------------------------------------------ The receiver checks the error code and takes whatever alerting, maintenance, or recovery action is appropriate. This message does not require a response. 12. Unrecognised or unexpected messages The default behaviour for unexpected or unrecognised received messages is to simply ignore them. For this reason it is important that all Half Pint messages which are resent on timeout, stick to the limit of 5 retries before abandoning. An entity supporting Half-Pint can send back a GeneralResponse message with a ResponseType of UnexpcetedMessage or UnrecognisedMessage if it wishes. 13. Message set transported directly on UDP OÆDoherty Page 1 Internet Draft Half-Pint July 2001 While how a Half-Pint message is transported is not specified, and many different options are available, transport over UDP is both simple and likely so it is worth mentioning briefly. With UDP the Half-Pint messages can be transported directly on top of UDP over IP. Each UDP datagram should contain one half-pint message. As is common practice (for example see SIP spec [2]), datagrams should not be larger than the path maximum transmission unit (MTU) or 1500 bytes if the MTU is unknown. However, implementations must be able to handle messages up to the maximum datagram size for UDP which is 65,535 bytes. 14. Versioning and Extending the Half-Pint Protocol All new versions of Half-Pint should be backwardly compatible û i.e. they must support the message format and procedures from previous versions. Therefore, if a Half-Pint implementation receives a message from a previous Version it should handle the message as specified in the spec for the previous version. If a Half-Pint implementation receives a message from a version newer than itself, it responds with a General Response message indicating which version it supports. The sender of the message can decide how to react to this, perhaps by reverting to an earlier version of the message or by abandoning the request. 15. Examples Click to Call service --------------------- A common requirement is to provide a facility for a web user to click on a button and have a call set up between them and the web site owner (or some other party). This type of service can easily be realised with the Half Pint Protocol as follows: 1) The user clicks on a button on æACME incÆ website to request to speak to a company representative. 2) The web application generates a CreateCall message with the users number as the Calling number and the companyÆs Call Centre as the Called number. The users number which is put in the message can either be prompted for or retrieved from some database about the user. 3) The telephony Proxy receives the message and generates the call. 4) The Users phone rings and when they answer it the call is placed to the companyÆs Call Centre. 5) The Call Centre representative answers and is connected to the user. Information such as the users account details, if they are a regular customer, can be made available based on the Calling users CLI. Call Notification Service ------------------------- A user working on their computer, or watching their television can have an alert pop up on the computer or TV screen alerting them about an incoming OÆDoherty Page 1 Internet Draft Half-Pint July 2001 call and indicating who is calling. The user may decide to answer the call, send it to voicemail or forward it to some other number. The flow for this service is as follows: 1) The user registers with the telephony Proxy for the call alerting service. 2) When a call comes in for the user a Call Alert message is sent to the devices specified in the Call Alerting registration. 3) The user decides what action to take and if they decide, for example, to send the call to voicemail, the user interacts with the computer or the TV GUI and a CallAlertResponse message is generated and sent back to the Telephony Proxy. Note that the user will also hear their phone ring (providing they are in the vicinity of it, of course !) when the call arrives. When some action is taken such as forwarding to Voicemail, the ringing stops. Conference Call Application --------------------------- A web based conference call controller service might work as follows: 1) The organizer of the conference call enters the details of the conference call into a web based conference service. They might schedule a particular conference for the following day at noon. They also may specify email addresses of the other participants so that they can be notified of the call by email. The web conference controller application sends a BookConferenceCall message to the telephony proxy with the appropriate details. 2) The next day the telephony proxy creates the conference call (or takes control of the conference equipment û the call itself may not be created until the first participant dials in depending on the way the telephony system works) at 11:55 and waits for the host or participants to dial in. If other participants dial in before the host they will be not be allowed enter the conference until the host has arrived and will be put on hold (and likely will be able to listen to either some advertising/branding or æGreensleevesÆ). 3) The host and participants can enter the conference either by clicking on a URL included in the email notification, which will cause the web conferencing controller application to use the CreateCall message to add the user to the conference call, or by dialing directly a number specified in the email. 16. Acknowledgements This protocol is a simple extension and formalization of work done in Nortel Networks by the following people: Dave Armstrong, Steve Perkins, Laurent Phelep, John Storrie and Bryan Miller. 17. References 1 Bradner, S., "The Internet Standards Process -- Revision 3", BCP 9, RFC 2026, October 1996. OÆDoherty Page 1 Internet Draft Half-Pint July 2001 2 Handley, et al., "RFC 2543 SIP: Session Initiation Protocol March 1999ö 3 Crocker & Overell, ôAugmented BNF for Syntax Specifications: ABNFö AuthorÆs Address Mick OÆDoherty Nortel Networks Concorde Road Maidenhead Berkshire SL6 4AG England mdoherty@nortelnetworks.com Appendix A û Example HTTP servlets The example code below consists of three classes. It does not fully implement a web server based implementation of Half Pint, but it gives enough to suggest one example of an architecture that could. The first Class is CreateCall which extends the standard HTTPServlet class and based on the parameters it retrieves, builds and sends a Half Pint CreateCall message. If the user has asked for confirmation of the create call message, a HTML redirect page is sent back to the user to refer the user to the servlet implemented by the third class below. The second class is a message handler that simply waits on a particular port (7071 in this case) and stores received half pint messages into a hash table. The third class is another servlet that is invoked by a redirect HTML Meta tag from the first servlet. It regularly checks the hash table that the second class is populating with received half pint messages, for the message that matches the transaction id of the original Half Pint message sent by the first class. When it finds the message it removes it from the hash table and informs the user that the call was created successfully. First Class û CreateCall ------------------------ import java.io.*; import java.text.*; import java.util.*; import java.net.*; import javax.servlet.*; import javax.servlet.http.*; OÆDoherty Page 1 Internet Draft Half-Pint July 2001 /** * CreateCall Servlet * * Instances of this class, like all servlets, wait around for for their doPost or doGet * methods to be invoked after they have been initialised. doGet will build and send a * Half-Pint CreateCall message. doPost simply calls doGet. * */ public class CreateCall extends HttpServlet { /* init method */ public void init() throws ServletException { getServletContext().setAttribute("HalfPint.incomingMsgHndlrRunning", "No"); } /* doGet method - main method of the servlet called by the Servlet engine or through doPost */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { PrintWriter out = response.getWriter(); response.setContentType("text/html"); /* Build the message based on the parameters and the environment variables. Note - transactioID's should really be created locally */ CreateCallMessage ccMessage = new CreateCallMessage(); ccMessage.halfPintVersion = "1.0"; ccMessage.addressee = request.getParameter("addresse"); ccMessage.transactionID = request.getParameter("transactionID"); ccMessage.authenticationInfo = request.getParameter("authenticationInfo"); ccMessage.messageType = "CreateCall"; ccMessage.callingParty = request.getParameter("callingParty"); ccMessage.calledParty = request.getParameter("calledParty"); ccMessage.completionNotifictaion = request.getParameter("completionNotifictaion"); OÆDoherty Page 1 Internet Draft Half-Pint July 2001 /* Send the message. Note some of the setup could be moved to init code */ byte[] messageBytes = (ccMessage.buildMessage()).getBytes(); InetAddress telephonyProxyAddress = InetAddress.getByName("X.X.X.X"); int telephonyProxyPort = 7071; DatagramPacket packet = new DatagramPacket(messageBytes, messageBytes.length, telephonyProxyAddress, telephonyProxyPort); DatagramSocket ds = new DatagramSocket(); ds.send( packet ); ds.close(); /* Is confirmation required ? */ if (ccMessage.completionNotifictaion.equalsIgnoreCase("yes") | ccMessage.completionNotifictaion.equalsIgnoreCase("y") ) { /* Spawn the message waiting handler first checking to see if it is already alive */ if ( ((String) getServletContext().getAttribute("HalfPint.incomingMsgHndlrRunning")).equals IgnoreCase("No") ) { // there are better ways to do this getServletContext().setAttribute("HalfPint.incomingMsgHndlrRunning", "Yes"); IncomingHPMsgHandler incomingMsgHndlr = new IncomingHPMsgHandler(); Thread incomingMsgHndlrThread = new Thread(incomingMsgHndlr); incomingMsgHndlrThread.start(); } /* Generate the wait page */ out.println("Page to wait and check for responses"); out.println(""); out.println(""); out.println(""); out.println("If your browser doesn't automatically refresh within a few seconds"); out.println("you may want check href=\"http:XXXXXX\"manually every few seconds"); out.println(""); } else { /* Generate the 'job done' page */ out.println(" "); out.println("

Job done - call created

"); out.println(" "); OÆDoherty Page 1 Internet Draft Half-Pint July 2001 } out.close(); } /* doPost method - simply calls doGet */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { doGet(request, response); } } Second Class û IncomingHPMsgHandler ----------------------------------- import java.net.*; /** * IncomingHPMsgHndlr * * This Class represents an object which sits around waiting for incoming Half-Pint messages * and then stores them so others can check them. * */ public class IncomingHPMsgHandler implements Runnable { public void run() { /* Get a reference to the shared singleton ReceivedMessages instance */ ReceivedHalfPintMsgs receivedMsgsHashtable = ReceivedHalfPintMsgs.getInstance(); HalfPintMessage receivedMessage = null; DatagramSocket ds = null; /* Bind to port */ try { ds = new DatagramSocket(7071); } catch (Exception e) { System.out.println("error opening socket in IncomingHPMsgHndlr"); return; } OÆDoherty Page 1 Internet Draft Half-Pint July 2001 /* When an incoming message is received store it */ while (true) { DatagramPacket packet = new DatagramPacket( new byte [1024], 1024); try { ds.receive(packet); } catch (Exception e) { System.out.println("Exception is" + e); System.out.println("error receiving packet in IncomingHPMsgHndlr"); } receivedMessage = HalfPintMessage.createHPMessage(new String(packet.getData())); if (receivedMessage != null) { receivedMsgsHashtable.addReceivedMsg(receivedMessage); } else System.out.println("received message was null"); } } } Third Class û CreateCallConfirmationCheck ----------------------------------------- import java.io.*; import java.text.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; /** * CreateCallConfirmationCheck Servlet * * Instances of this class, like all servlets, wait around for for their doPost or doGet * methods to be invoked after they have been initialised. doGet check to see if a confirmation # * has been received for the Create Call message corresponding to the transaction rceived. * */ public class CreateCallConfirmationCheck extends HttpServlet { OÆDoherty Page 1 Internet Draft Half-Pint July 2001 /* doGet method - main method of the servlet called by the Servlet engine or through doPost */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { PrintWriter out = response.getWriter(); response.setContentType("text/html"); /* Get a reference to the shared ReceivedMessages instance */ ReceivedHalfPintMsgs receivedMsgsHashtable = ReceivedHalfPintMsgs.getInstance(); /* The message type we are expecting is a GeneralResponseMessage but use a HalfPint message for now in case the wrong message type was received */ HalfPintMessage confirmMsgReceived; /* Check hashtable to see if the confirmation message has been received and if it has reply that it has. If not then send a waiting page */ String transactionID = request.getParameter("transactionID"); if(transactionID == null) { /* Problem - tell the client */ out.println(" "); out.println("

Error - no transaction id !!!

"); out.println(" "); } else { if ((confirmMsgReceived = receivedMsgsHashtable.getReceivedMsg(transactionID)) == null) { /* Generate the wait page as the confirmation has not been received yet */ out.println("Page to wait and check for responses"); out.println(""); out.println(""); out.println(""); out.println("If your browser doesn't automatically refresh within a few seconds"); out.println("you may want check href=\"http:XXXXXX\"manually every few seconds"); out.println(""); } else { /* Check that the call was created successfully */ OÆDoherty Page 1 Internet Draft Half-Pint July 2001 if(!(confirmMsgReceived.messageType.equalsIgnoreCase("GeneralResponse") )) { // UnexpectedMessageHandling; System.out.println("Unexected message when waiting for a General response to a Create Call"); } else { /* Check to see if the received response is an 'OK' */ GeneralResponseMessage grm = (GeneralResponseMessage) confirmMsgReceived; if (grm.responseType.equalsIgnoreCase("ok")) { /* Generate the 'job done' page */ out.println(" "); out.println("

Job done - call created

"); out.println(" "); } else { /* Call was not created - tell the client */ out.println(" "); out.println("

Call not created !!!

"); out.println("

Error code :" + grm.responseType + "

"); out.println(" "); } } } } out.close(); } /* doPost method - simply calls doGet */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { doGet(request, response); } } OÆDoherty Page 1