001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.camel.component.syslog;
019    
020    import java.net.InetAddress;
021    import java.net.UnknownHostException;
022    import java.nio.ByteBuffer;
023    import java.util.Calendar;
024    import java.util.Date;
025    import java.util.GregorianCalendar;
026    import java.util.HashMap;
027    import java.util.Map;
028    
029    import org.apache.camel.Converter;
030    import org.apache.commons.logging.Log;
031    import org.apache.commons.logging.LogFactory;
032    
033    public final class Rfc3164SyslogConverter {
034    
035        private static final transient Log LOG = LogFactory.getLog(Rfc3164SyslogConverter.class);
036    
037        private static enum MONTHS {
038            jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec
039        }
040    
041        private static Map<String, MONTHS> monthValueMap = new HashMap<String, MONTHS>() {
042            {
043                put("jan", MONTHS.jan);
044                put("feb", MONTHS.feb);
045                put("mar", MONTHS.mar);
046                put("apr", MONTHS.apr);
047                put("may", MONTHS.may);
048                put("jun", MONTHS.jun);
049                put("jul", MONTHS.jul);
050                put("aug", MONTHS.aug);
051                put("sep", MONTHS.sep);
052                put("oct", MONTHS.oct);
053                put("nov", MONTHS.nov);
054                put("dec", MONTHS.dec);
055            }
056        };
057    
058        private Rfc3164SyslogConverter() {
059            //Utility class
060        }
061    
062        @Converter
063        public static String toString(SyslogMessage message) {
064            StringBuilder sbr = new StringBuilder();
065            sbr.append("<");
066            if (message.getFacility() == null) {
067                message.setFacility(SyslogFacility.USER);
068            }
069            if (message.getSeverity() == null) {
070                message.setSeverity(SyslogSeverity.INFO);
071            }
072            if (message.getHostname() == null) {
073                //This is massively ugly..
074                try {
075                    message.setHostname(InetAddress.getLocalHost().toString());
076                } catch (UnknownHostException e) {
077                    message.setHostname("UNKNOWN_HOST");
078                }
079            }
080            sbr.append(message.getFacility().ordinal() * 8 + message.getSeverity().ordinal());
081            sbr.append(">");
082            if (message.getTimestamp() == null) {
083                message.setTimestamp(new Date());
084            }
085    
086            //SDF isn't going to help much here.
087    
088            Calendar cal = GregorianCalendar.getInstance();
089            cal.setTime(message.getTimestamp());
090    
091            String firstLetter = MONTHS.values()[cal.get(Calendar.MONTH)].toString().substring(0, 1);  // Get first letter
092            String remainder = MONTHS.values()[cal.get(Calendar.MONTH)].toString()
093                .substring(1);    // Get remainder of word.
094            String capitalized = firstLetter.toUpperCase() + remainder.toLowerCase();
095    
096            sbr.append(capitalized);
097            sbr.append(" ");
098    
099            if (cal.get(Calendar.DAY_OF_MONTH) < 10) {
100                sbr.append(" ").append(cal.get(Calendar.DAY_OF_MONTH));
101            } else {
102                sbr.append(cal.get(Calendar.DAY_OF_MONTH));
103            }
104    
105            sbr.append(" ");
106    
107            if (cal.get(Calendar.HOUR_OF_DAY) < 10) {
108                sbr.append("0").append(cal.get(Calendar.HOUR_OF_DAY));
109            } else {
110                sbr.append(cal.get(Calendar.HOUR_OF_DAY));
111            }
112            sbr.append(":");
113    
114            if (cal.get(Calendar.MINUTE) < 10) {
115                sbr.append("0").append(cal.get(Calendar.MINUTE));
116            } else {
117                sbr.append(cal.get(Calendar.MINUTE));
118            }
119            sbr.append(":");
120    
121            if (cal.get(Calendar.SECOND) < 10) {
122                sbr.append("0").append(cal.get(Calendar.SECOND));
123            } else {
124                sbr.append(cal.get(Calendar.SECOND));
125            }
126            sbr.append(" ");
127    
128            sbr.append(message.getHostname());
129            sbr.append(" ");
130            sbr.append(message.getLogMessage());
131            return sbr.toString();
132        }
133    
134        @Converter
135        public static SyslogMessage toSyslogMessage(String body) {
136            return parseMessage(body.getBytes());
137        }
138    
139        public static SyslogMessage parseMessage(byte[] bytes) {
140            ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
141            byteBuffer.put(bytes);
142            byteBuffer.rewind();
143    
144            SyslogMessage syslogMessage = new SyslogMessage();
145            Character charFound = (char) byteBuffer.get();
146    
147            while (charFound != '<') {
148                //Ignore noise in beginning of message.
149                charFound = (char) byteBuffer.get();
150            }
151            char priChar = 0;
152            if (charFound == '<') {
153                int facility = 0;
154    
155                while (Character.isDigit(priChar = (char) (byteBuffer.get() & 0xff))) {
156                    facility *= 10;
157                    facility += Character.digit(priChar, 10);
158                }
159                syslogMessage.setFacility(SyslogFacility.values()[facility >> 3]);
160                syslogMessage.setSeverity(SyslogSeverity.values()[facility & 0x07]);
161            }
162    
163            if (priChar != '>') {
164                //Invalid character - this is not a well defined syslog message.
165                LOG.error("Invalid syslog message, missing a > in the Facility/Priority part");
166            }
167    
168            //Done parsing severity and facility
169            //<169>Oct 22 10:52:01 TZ-6 scapegoat.dmz.example.org 10.1.2.3 sched[0]: That's All Folks!
170            //Need to parse the date.
171    
172            /**
173             The TIMESTAMP field is the local time and is in the format of "Mmm dd
174             hh:mm:ss" (without the quote marks) where:
175    
176             Mmm is the English language abbreviation for the month of the
177             year with the first character in uppercase and the other two
178             characters in lowercase.  The following are the only acceptable
179             values:
180    
181             Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
182    
183             dd is the day of the month.  If the day of the month is less
184             than 10, then it MUST be represented as a space and then the
185             number.  For example, the 7th day of August would be
186             represented as "Aug  7", with two spaces between the "g" and
187             the "7".
188    
189             hh:mm:ss is the local time.  The hour (hh) is represented in a
190             24-hour format.  Valid entries are between 00 and 23,
191             inclusive.  The minute (mm) and second (ss) entries are between
192             00 and 59 inclusive.
193    
194    
195             */
196    
197            char[] month = new char[3];
198            for (int i = 0; i < 3; i++) {
199                month[i] = (char) (byteBuffer.get() & 0xff);
200            }
201            charFound = (char) byteBuffer.get();
202            if (charFound != ' ') {
203                //Invalid Message - missing mandatory space.
204                LOG.error("Invalid syslog message, missing a mandatory space after month");
205            }
206            charFound = (char) (byteBuffer.get() & 0xff);
207    
208            int day = 0;
209            if (charFound == ' ') {
210                //Extra space for the day - this is okay.
211                //Just ignored per the spec.
212            } else {
213                day *= 10;
214                day += Character.digit(charFound, 10);
215            }
216    
217            while (Character.isDigit(charFound = (char) (byteBuffer.get() & 0xff))) {
218                day *= 10;
219                day += Character.digit(charFound, 10);
220            }
221    
222            int hour = 0;
223            while (Character.isDigit(charFound = (char) (byteBuffer.get() & 0xff))) {
224                hour *= 10;
225                hour += Character.digit(charFound, 10);
226            }
227    
228            int minute = 0;
229            while (Character.isDigit(charFound = (char) (byteBuffer.get() & 0xff))) {
230                minute *= 10;
231                minute += Character.digit(charFound, 10);
232            }
233    
234            int second = 0;
235            while (Character.isDigit(charFound = (char) (byteBuffer.get() & 0xff))) {
236                second *= 10;
237                second += Character.digit(charFound, 10);
238            }
239    
240            //The host is the char sequence until the next ' '
241    
242            StringBuilder host = new StringBuilder();
243            while ((charFound = (char) (byteBuffer.get() & 0xff)) != ' ') {
244                host.append(charFound);
245            }
246    
247            syslogMessage.setHostname(host.toString());
248    
249            StringBuilder msg = new StringBuilder();
250            while (byteBuffer.hasRemaining()) {
251                charFound = (char) (byteBuffer.get() & 0xff);
252                msg.append(charFound);
253            }
254    
255            Calendar calendar = new GregorianCalendar();
256            calendar.set(Calendar.MONTH, monthValueMap.get(String.valueOf(month).toLowerCase()).ordinal());
257            calendar.set(Calendar.DAY_OF_MONTH, day);
258            calendar.set(Calendar.HOUR_OF_DAY, hour);
259            calendar.set(Calendar.MINUTE, minute);
260            calendar.set(Calendar.SECOND, second);
261    
262            syslogMessage.setTimestamp(calendar.getTime());
263    
264            syslogMessage.setLogMessage(msg.toString());
265            if (LOG.isTraceEnabled()) {
266                LOG.trace("Syslog message : " + syslogMessage.toString());
267            }
268    
269            return syslogMessage;
270        }
271    }