001/* 002 * Copyright 2014 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.sms.broker; 017 018import java.io.IOException; 019import java.io.InputStream; 020import java.text.SimpleDateFormat; 021import java.util.Date; 022import java.util.Set; 023 024import org.apache.avalon.framework.configuration.Configurable; 025import org.apache.avalon.framework.configuration.Configuration; 026import org.apache.avalon.framework.configuration.ConfigurationException; 027import org.apache.avalon.framework.context.Context; 028import org.apache.avalon.framework.context.ContextException; 029import org.apache.avalon.framework.context.Contextualizable; 030import org.apache.avalon.framework.service.ServiceException; 031import org.apache.avalon.framework.service.ServiceManager; 032import org.apache.cocoon.ProcessingException; 033import org.apache.cocoon.components.ContextHelper; 034import org.apache.cocoon.environment.Request; 035import org.apache.commons.codec.digest.DigestUtils; 036import org.apache.commons.io.IOUtils; 037import org.apache.commons.io.input.BOMInputStream; 038import org.apache.commons.lang.StringUtils; 039import org.apache.http.Consts; 040import org.apache.http.HttpEntity; 041import org.apache.http.HttpResponse; 042import org.apache.http.client.config.RequestConfig; 043import org.apache.http.client.methods.HttpPost; 044import org.apache.http.entity.ContentType; 045import org.apache.http.entity.mime.MultipartEntityBuilder; 046import org.apache.http.entity.mime.content.InputStreamBody; 047import org.apache.http.entity.mime.content.StringBody; 048import org.apache.http.impl.client.CloseableHttpClient; 049import org.apache.http.impl.client.HttpClientBuilder; 050 051import org.ametys.core.user.UserIdentity; 052import org.ametys.core.user.UserManager; 053import org.ametys.runtime.config.Config; 054/*import org.apache.http.entity.mime.MultipartEntity; 055import org.apache.http.entity.mime.content.InputStreamBody; 056import org.apache.http.entity.mime.content.StringBody;*/ 057 058/** 059 * Broker implementation using the "etoile diese" web service 060 */ 061public class EtoileDieseBroker extends LoggerBroker implements Configurable, Contextualizable 062{ 063 private static SimpleDateFormat __DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss"); 064 065 /** Url of Etoile diese's send sms web service in bulk mode (the recipients are listed in a CSV file) */ 066 private String _etoileDieseBulkWSUrl; 067 068 /** The Avalon context */ 069 private Context _context; 070 071 /** The users manager */ 072 private UserManager _userManager; 073 074 @Override 075 public void configure(Configuration configuration) throws ConfigurationException 076 { 077 _etoileDieseBulkWSUrl = configuration.getChild("etoilediese-bulk-ws-url").getValue(); 078 } 079 080 @Override 081 public void contextualize(Context context) throws ContextException 082 { 083 _context = context; 084 } 085 086 @Override 087 public void service(ServiceManager manager) throws ServiceException 088 { 089 super.service(manager); 090 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 091 } 092 093 @Override 094 public void send(Set<String> phoneNumbersList, String message, String listId) throws Exception 095 { 096 super.send(phoneNumbersList, message, listId); 097 098 String username = Config.getInstance().getValueAsString("org.ametys.plugins.sms.broker.user"); 099 String password = Config.getInstance().getValueAsString("org.ametys.plugins.sms.broker.password"); 100 String now = __DATE_FORMAT.format(new Date()); 101 UserIdentity user = _currentUserProvider.getUser(); 102 String userFullName = _userManager.getUser(user.getPopulationId(), user.getLogin()).getFullName(); 103 104 String computedPwd = DigestUtils.md5Hex(password + username + now); 105 106 RequestConfig requestConfig = RequestConfig.custom().build(); 107 108 try (CloseableHttpClient httpclient = HttpClientBuilder.create().useSystemProperties().setDefaultRequestConfig(requestConfig).build(); 109 InputStream csvFile = _getCSVFile(phoneNumbersList, message)) 110 { 111 MultipartEntityBuilder builder = MultipartEntityBuilder.create(); 112 113 // User 114 builder.addPart("u", new StringBody(username, ContentType.create("text/plain", Consts.UTF_8))); 115 // Password 116 builder.addPart("p", new StringBody(computedPwd, ContentType.create("text/plain", Consts.UTF_8))); 117 // Current date for MD5 sum 118 builder.addPart("date", new StringBody(now, ContentType.create("text/plain", Consts.UTF_8))); 119 // Category 120 builder.addPart("c", new StringBody(getCategory(listId), ContentType.create("text/plain", Consts.UTF_8))); 121 // Message type 122 builder.addPart("f", new StringBody("sms", ContentType.create("text/plain", Consts.UTF_8))); 123 // CSV file 124 builder.addPart("file", new InputStreamBody(csvFile, ContentType.create("text/csv", Consts.UTF_8), "sms.csv")); 125 // Additionnal information 126 builder.addPart("sender-login", new StringBody(UserIdentity.userIdentityToString(user), ContentType.create("text/plain", Consts.UTF_8))); 127 builder.addPart("sender-fullname", new StringBody(userFullName, ContentType.create("text/plain", Consts.UTF_8))); 128 129 HttpEntity entity = builder.build(); 130 131 // Prepare a request object 132 HttpPost postRequest = new HttpPost(_etoileDieseBulkWSUrl); 133 postRequest.setEntity(entity); 134 135 // Execute the request 136 HttpResponse httpResponse = httpclient.execute(postRequest); 137 138 if (httpResponse.getStatusLine().getStatusCode() == 200) 139 { 140 HttpEntity responseEntity = httpResponse.getEntity(); 141 if (responseEntity != null) 142 { 143 try (InputStream is = responseEntity.getContent();) 144 { 145 String response = IOUtils.toString(is, "ISO-8859-1"); 146 IOUtils.closeQuietly(is); 147 148 if (response.contains("bulk")) 149 { 150 getLogger().info("EtoileDiese's response: " + response); 151 return; 152 } 153 else 154 { 155 throw new ProcessingException("EtoileDiese failed to process delivering with the following message: " + response); 156 } 157 } 158 } 159 throw new ProcessingException("EtoileDiese has not sent any response"); 160 } 161 162 throw new ProcessingException("Unable to call the web service: " + _etoileDieseBulkWSUrl + ", response: " + httpResponse.getStatusLine()); 163 } 164 } 165 166 @Override 167 public String getPhoneNumberFromStopRequest() throws Exception 168 { 169 Request request = ContextHelper.getRequest(_context); 170 String responseText = request.getParameter("txt"); 171 String phoneNumber = request.getParameter("num"); 172 173 if (StringUtils.containsIgnoreCase(responseText, "stop")) 174 { 175 if (phoneNumber != null) 176 { 177 return "+" + phoneNumber; 178 } 179 180 throw new ProcessingException("EtoileDiese did not provide a phone number for the stop sms request"); 181 } 182 183 getLogger().info("The sms response of number [" + (phoneNumber != null ? phoneNumber : "unknown") + "] does not contain the 'stop' string: " + (responseText != null ? responseText : "[empty message]")); 184 return null; 185 } 186 187 /** 188 * Creates and return a CSV file from a list of numbers and a message 189 * @param phoneNumbersList the list of phone numbers to send a SMS to 190 * @param message the message to send 191 * @return the csv file 192 * @throws IOException if an error occurs while converting the list of phone numbers to an input stream 193 */ 194 private InputStream _getCSVFile(Set<String> phoneNumbersList, String message) throws IOException 195 { 196 StringBuffer sb = new StringBuffer(); 197 String banalizedNewLinesMessage = message.replace("\n", "\\n"); 198 199 for (String phoneNumber : phoneNumbersList) 200 { 201 sb.append(phoneNumber); 202 sb.append(" ; "); 203 sb.append(banalizedNewLinesMessage); 204 sb.append('\n'); 205 } 206 207 return new BOMInputStream(IOUtils.toInputStream(sb.toString(), "UTF-8")); 208 } 209}